Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Неопределенные классы в динамической библиотеке
Форум на CrossPlatform.RU > Разработка > С\С++
Count0
Доброго времени суток.
Возникла следующая проблема:
Для исследовательского ПО разрабатывается программный код использующий динамические библиотеки для хранения взаимозаменяемых алгоритмов.
Поскольку цель ПО - проведение исследований, то в дальнейшем планируется появление неограниченного числа новых библиотек, ни сигнатуру ни корректность функций которых не представляется возможным проконтролировать.
Поэтому пришлось разработать иерархию шаблонных классов которые принимают указатель на функцию библиотеки, описание допустимых диапазонов ее параметров, на основе которых генерирую пользовательский интерфейс для инициализации параметров, а затем по требованию пользователя запускают ее по указателю.
Таким образом, все что требуется от разработчика библиотеки создать функцию обертку которая передает все данные в синглтон-регистратор.
Вопрос в том: можно ли избежать включения всех исходников данной иерархии в динамическую библиотеку, оставив их только в основной части программы, а вбиблиотеках ограничиться опережающими объявлениями.
Iron Bug
ответ: нет.
шаблоны используются только на этапе компиляции.
впрочем, если подождать лет так пять.... в последних дискуссиях по стандарту С++14 вроде как обсуждалось динамическое использование шаблонов ;)
Count0
То есть придется потребовать добавлять всю иерархию в код библиотеки. А если та самая функция-регистратор обращается к singleton, чтобы передать ему данные о функцие-алгоритме, не вызовет ли повторное определение singleton в библиотеках ошибок? И корректно ли будут передаваться данные именно в singleton, инициализированный в приложении.
Iron Bug
смотря что там в иерархии. всё, где шаблоны, которые как-либо прямо или косвенно используются в вызовах юзера - да.
то есть, шаблон лишь определяет, как генерить класс. а компилятор генерит его, по собранным в коде вызовам. если есть использование шаблона с классом А - будет просто сгенерён класс, который есть результат применения шаблона к этому классу. и так для всех сочетаний.

вообще говоря, вопрос сигнатуры решается просто: передачей указателя. а уж на что он там указывает - это можно описывать в документации к библиотеке алгоритмов, например.
Count0
Большое спасибо Вам.

Тут так не получится, указатель же будет void*, основной код о сигнатуре знает только возвращаемый тип и первый параметр. А динамически сгенерировать код вызова функции по описанию насколько я знаю невозможно.
Iron Bug
я не очень понимаю суть задачи. понятие плагина существует и плагины создаются по довольно стандартной схеме. на этот счёт есть много статей в сети и тут на форуме где-то были обсуждения.
если известен только первый параметр - то stdarg.h c va_list, va_arg и т.д. так работает printf, например: читает первый аргумент и вытаскивает все остальные из стека.
всё равно конечный потребитель данных знает их назначение и знает, как использовать переданные параметры.

если можешь на пальцах пояснить суть задачи - может, подскажу что-то конкретное.
ViGOur
Завтра мне напомните, у меня есть идейка как подобное реализовать, сейчас меня просто вырубает (спать хочется), потому лучше на завтра отложить...
ViGOur
Ну вот сделал набросок:

"factory.h"
#ifndef FACTORY_H
#define FACTORY_H

#include <string>

class CAlgorithmBase;

template<typename TYPE>
class CFactoryBase
{
public:
    virtual TYPE *createInstance() = 0;
    virtual void destroyInstance(void *) = 0;
};

template<typename TYPE, typename BASETYPE>
class CFactoryAlgorithm : public CFactoryBase<BASETYPE>
{
public:
    TYPE *createInstance()
    {
        TYPE *pType = new TYPE();
        return pType;
    }
    void destroyInstance(void *p)
    {
        delete static_cast<TYPE*>(p);
    }
};


template<typename TYPE>
CFactoryBase<CAlgorithmBase>* MakeFactoryAlgorithm()
{
    static CFactoryAlgorithm<TYPE, CAlgorithmBase> fac;
    return &fac;
}

struct sFactoryAssocAlgorithm
{
    std::string szNameAlgorithm;
    CFactoryBase<CAlgorithmBase> *pFactory;
};


#endif // FACTORY_H
"main.cpp"
#include "factory.h"
#include <iostream>

class CAlgorithmBase
{
protected:
    std::string m_szName;
public:
    CAlgorithmBase(){m_szName = "CAlgorithmBase";}
    void print(){std::cout << "Algorithm: " << m_szName << std::endl;}
};

class CAlgo0 : public CAlgorithmBase
{
public:
    CAlgo0(){m_szName = "CAlgo0";}
};

class CAlgo1 : public CAlgorithmBase
{
public:
    CAlgo1(){m_szName = "CAlgo1";}
};

class CAlgo2 : public CAlgorithmBase
{
public:
    CAlgo2(){m_szName = "CAlgo2";}
};

sFactoryAssocAlgorithm g_algos[]=
{
    {"algo0", MakeFactoryAlgorithm<CAlgo0>()},
    {"algo1", MakeFactoryAlgorithm<CAlgo1>()},
    {"algo2", MakeFactoryAlgorithm<CAlgo2>()}
};

int main(int argc, char *argv[])
{
    int nCount = sizeof(g_algos)/sizeof(*g_algos);
    std::cout << "Algorithm count: " << nCount << std::endl;
    for( int n = 0; n < nCount; n++ )
    {
        CAlgorithmBase *pObj = g_algos[n].pFactory->createInstance();
        pObj->print();
        g_algos[n].pFactory->destroyInstance( pObj );
    }

    return 0;
}
"результат работы"
Algorithm count: 3
Algorithm: CAlgo0
Algorithm: CAlgo1
Algorithm: CAlgo2

Ну и по коду видно, что тебе остается только динамически заполнить массив g_algos, каждый элемент которого может возвращать загружаемая библиотека с тем или иным алгоритмом и все, если я конечно же тебя правильно понял...
Iron Bug
ViGOur, тут вопрос в том, как передавать параметры алгоритмам из библиотеки.
допустим, тебе надо протестировать некий заранее неизвестный алгоритм по нескольким параметрам. ты знаешь, сколько параметров и какие граничные значения они могут принимать. вопрос в том, как передать параметры в юзерскую функцию, без применения шаблонов и засвечивания всей внутрненней структуры.
в принципе, есть bind - уже готовое решение такой задачи, но это тоже шаблон, поэтому все классы, которые он будет использовать, должны быть видны снаружи при компиляции.
ViGOur
Тут мне кажется немного не правильный подход.
Зачем заморачиваться на функциях, когда библиотека может отдавать указатель на класс нужного в данный момент алгоритма, на основе которого будет строится GUI для данного алгоритма и отрабатывать сам алгоритм?
Причем класс может быть как стандартным (для ленивых), который работает с нужной функцией, так и расширенным пользователем (с описанием параметров и ...).

Другими словами, мы задаем некие правила, которым должны следовать пользователи, и в данном случае нам нужно будет давать пользователям только базовый класс CAlgorithmBase с правилами его использования и все...
Iron Bug
GUI тут вообще ни при чём. я так понимаю, что это обычный софт для тестирования работы алгоритмов.
надо не только создать некий объект с алгоритмом, но и вызывать его, с переменными параметрами. а вот тут-то и происходит затык.
короче говоря, это вызов функции по указателю, с передачей заранее неизвестного числа аргументов. с dll это не проканает. тут надо городить какой-то огород со специальными методами передачи параметров, чтобы унифицировать вызовы.
ViGOur
не обязательно, можно так же передавать указатель на структуру, в которой будут перечисленны параметры...
А лучше как я уже говорил, в указателе на класс! А в классе уже сделать стандартная реализация получения функции алгоритма, и параметром с их описанием! ;)
В предложенном мной варианте, как раз все просто!
Iron Bug
ну да. только без темплейтов получится монстр. потому что обработка разных типов параметров займёт туеву хучу кода. обработка совершенно одинакова для всех типов, но темплейты в случае с библиотеками не работают. нельзя экспортировать классы с шаблонами.
ViGOur
Блин, блинский, это то у меня совсем из головы-то и вылетело!
Беру тайм-аут на подумать...
Flex Ferrum
(зашёл сюда по приглашению ViGOur'а)
Мне видится такой вариант ответа на поставленный в первом посте вопрос - скомбинировать compiletime-анализ в runtime.
Со стороны плагина делается следующее:
1. Для плагина делается небольшой шаблонный фреймворк, основная задача которого - принять сигнатуру тестируемой функции, констрейнты на аргументы и превратить это в универсальный invoke-ер. При этом фреймворк (используя typeinfo и typename для сигнатуры функции и для каждого аргумента) формирует набор структур, которые однозначно и с необходимой детализацией эту функцию описывают. В общем итоге плагин передаёт приложению следующую информацию:
- Полученные на этапе компиляции структуры, описывающие типы аргументов функции и констрейнты на них;
- Указатель на функцию-invoker (с точно заданной сигнатурой), которая получает на массив аргументов и передаёт их в тестируемую функцию.
2. Со стороны приложения делается runtime-анализатор данных, полученных от плагина. На основании этих данных строится GUI, прописываются валидаторы аргументов и т. д. Когда пользователь нажимает на кнопку "test" (или как она там будет называться), то введённые им аргументы превращаются в массив структур, и этот массив передаётся в invoke-ер. Там все элементы этого массива превращаются в аргументы тестируемой функции и делается собственно вызов.
Для добавления type safety можно использовать boost::variant для передачи значений аргументов (ведь GUI будет работать с конечным набором типов).
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.