Чтобы убедиться, что я поддерживаю ABI, я использую классы с явным vtable.
в myinterface / import.h
class MyInterface
{
public:
void doStuff()
{m_vt->doStuff(this);}
class Vtable
{
friend class MyInterface;
public:
bool init(void* module);
size_t abiVersionTagGet() const
{return abi_version_tag;}
private:
typedef void (*DoStuffFunc)(MyInterface* obj);
size_t abi_version_tag;
DoStuffFunc doStuff;
//etc
};
private:
Vtable* m_vt;
};
Указатели на функции внутри таблицы относятся к функциям экспорта динамической библиотеки, таким как
в myinterface / export.h:
class MyInterface;
extern "C"{
void EXPORT MyInterface_1doStuff(MyInterface* object);
}
в myinterfaceimpl.cpp (или как это называется)
#include "myinterface/export.h"
void MyInterface_1doStuff(MyInterface* object)
{
MyInterfaceImpl* _this=(MyInterfaceImpl*)object;
// ...
}
Теперь у меня есть 2 включаемых файла для каждого интерфейса. Как мне облегчить поддержание такой структуры.
РЕДАКТИРОВАТЬ: «Решение» Pimpl ниже, не решает проблему, так как он также полагается на автоматический vtable.
Как и просили в комментариях, вот пример использования макроса, чтобы добраться до середины пути;
Прежде чем начать, минусы:
gcc -E
прежде чем распространять его. Если вы сделаете это, вы должны изменить макросы цикла, чтобы включить завершающие символы новой строки).Итак, начнем с безобразного. Спрятать это в заголовке (я пытался использовать функции, которые могут быть использованы в другом месте);
#define EXPAND(a) a
#define ARGS_COUNT__(\
_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,\
_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,\
_64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,\
_48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,\
_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,\
_16,_15,_14,_13,_12,_11,_10, _9, _8, _7, _6, _5, _4, _3, _2, _1,\
N,...) N
#define ARGS_COUNT_(...) ARGS_COUNT__(__VA_ARGS__,\
96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,\
80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,\
64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,\
48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,\
32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,\
16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define ARGS_HEAD(a,...) a
#define ARGS_TAIL(a,...) (__VA_ARGS__)
#define FOREACH(macro,list) FOREACH_(ARGS_COUNT_ list,macro,list)
#define FOREACH_(n,macro,list) FOREACH__(n,macro,list)
#define FOREACH__(n,macro,list) FOREACH_##n(macro,list)
#define FOREACH_1(macro,list) EXPAND(macro EXPAND list)
#define FOREACH_2(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_1(macro,ARGS_TAIL list)
#define FOREACH_3(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_2(macro,ARGS_TAIL list)
#define FOREACH_4(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_3(macro,ARGS_TAIL list)
#define FOREACH_5(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_4(macro,ARGS_TAIL list)
#define FOREACH_6(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_5(macro,ARGS_TAIL list)
#define FOREACH_7(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_6(macro,ARGS_TAIL list)
#define FOREACH_8(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_7(macro,ARGS_TAIL list)
#define FOREACH_9(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_8(macro,ARGS_TAIL list)
#define FOREACH_10(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_9(macro,ARGS_TAIL list)
#define FOREACH_11(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_10(macro,ARGS_TAIL list)
#define FOREACH_12(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_11(macro,ARGS_TAIL list)
#define FOREACH_13(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_12(macro,ARGS_TAIL list)
#define FOREACH_14(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_13(macro,ARGS_TAIL list)
#define FOREACH_15(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_14(macro,ARGS_TAIL list)
#define FOREACH_16(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_15(macro,ARGS_TAIL list)
#define FOREACH_17(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_16(macro,ARGS_TAIL list)
#define FOREACH_18(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_17(macro,ARGS_TAIL list)
#define FOREACH_19(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_18(macro,ARGS_TAIL list)
#define FOREACH_20(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_19(macro,ARGS_TAIL list)
#define FOREACH_21(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_20(macro,ARGS_TAIL list)
#define FOREACH_22(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_21(macro,ARGS_TAIL list)
#define FOREACH_23(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_22(macro,ARGS_TAIL list)
#define FOREACH_24(macro,list) EXPAND(macro ARGS_HEAD list) FOREACH_23(macro,ARGS_TAIL list)
// etc. You need to repeat this up to the maximum number of loops you will need.
// maybe somebody can find a way to split the list in half, in which case
// the number of these would be dramatically reduced (n -> log_2(n))
Так что это? Хорошо ARGS_COUNT_
материал делает как следует из названия; это подсчитывает, сколько аргументов вы приводите. Это используется FOREACH
функция, которая используется так:
#define MY_MACRO(a,b,c) int t##a = b + c;
FOREACH( MY_MACRO, ((1,2,3),(4,5,6)) )
(обратите внимание на все скобки вокруг параметров). Это даст:
int t1 = 2 + 3;
int t4 = 5 + 6;
Здорово.
Итак, давайте использовать это для того, что вы делаете. Сначала мы составим большой список важных битов: (это должно быть в заголовке, который включен в оба заголовка)
// return type, function name, function parameters, parameter pass-through
#define MY_FUNCTIONS ( \
(void, doStuff, _1doStuff, (), ()), \
(int, someOtherFunction, _1someOtherFunction, (int a, float b), (a,b)) \
)
Немного ужасно, что нам нужно повторять параметры с типами и без них, но я не мог придумать опрятную альтернативу.
Итак, теперь петли:
class MyInterface {
public:
#define ADD_THIS(...) (this,##__VA_ARGS__)
#define MY_PASSTHROUGH(ret,name,fancy,args,pass) ret name args{m_vt->name ADD_THIS pass;}
FOREACH(MY_PASSTHROUGH,MY_FUNCTIONS)
class Vtable {
// ...
private:
size_t abi_version_tag;
#define ADD_THIS_P(...) (MyInterface*,##__VA_ARGS__)
#define MY_FUNCDEF(ret,name,fancy,args,pass) typedef ret (*name##Func)ADD_THIS_P args; name##Func name;
FOREACH(MY_FUNCDEF,MY_FUNCTIONS)
};
private:
Vtable* m_vt;
};
И в другом заголовке:
class MyInterface;
extern "C" {
#define ADD_THIS_P(...) (MyInterface*,##__VA_ARGS__)
#define MY_CFUNC(ret,name,fancy,args,pass) ret EXPORT MyInterface##fancy ADD_THIS_P args;
FOREACH(MY_CFUNC,MY_FUNCTIONS)
}
Я не уверен, что полностью понимаю ваше требование, но здесь идет.
Я предлагаю вам использовать идиому pimpl и объявить общий интерфейсный класс, такой как:
class IInterface
{
public:
class impl;
impl* pimpl;
public:
void DoSomthing();
void DoSomethingElse(int a);
void DoSomthingInteresting(const char*);
~IInterface();
};
Теперь каждая динамическая библиотека содержит фабричную функцию для создания этого интерфейса для вас:
IInterface* InterfaceFactory();
Внутренняя реализация каждой динамической библиотеки будет отличаться, но совместимость ABI будет сохранена. Внутри динамической библиотеки вы бы сделали это:
struct IInterface::impl
{
void DoSomething()
{
std::cout << "I did it!" << "\n";
}
void DoSomethingElse(int a)
{
std::cout << "The answer is: " << a << "\n";
}
void DoSomthingInteresting(const char* s)
{
std::cout << "The question is: " << s << "\n";
}
};
void IInterface::DoSomthing()
{
pimpl->DoSomething();
}
void IInterface::DoSomethingElse(int a)
{
pimpl->DoSomethingElse(a);
}
void IInterface::DoSomthingInteresting(const char* s)
{
pimpl->DoSomthingInteresting(s);
}
IInterface::~IInterface()
{
delete pimpl;
}
IInterface* InterfaceFactory()
{
IInterface* MyInterface = new IInterface();
MyInterface->pimpl = new IInterface::impl();
return MyInterface;
}