Я пишу библиотеку-оболочку C++ для ряда различных аппаратных библиотек для встраиваемых систем (на уровне прошивки), используя различные библиотеки от разных поставщиков (C или C++). API, предоставляемый файлами заголовков, не должен зависеть от поставщика... все библиотеки заголовков поставщиков не включены ни в один из моих файлов заголовков.
Обычный шаблон, который у меня есть, заключается в том, чтобы сделать данные члена поставщика непрозрачными, используя только указатель на некоторый «неизвестный» тип поставщика struct/class/typedef/pod.
// myclass.h
class MyClass
{
...
private:
VendorThing* vendorData;
};
и реализация (примечание: каждая реализация зависит от поставщика; у всех один и тот же файл *.h)
// myclass_for_vendor_X.cpp
#include "vendor.h"
... {
vendorData->doSomething();
or
VendorAPICall(vendorData,...);
or whatever
У меня проблема в том, что VendorThing
может быть много разных вещей. Это может быть класс, структура, тип или модуль. Я не знаю, и я не хочу заботиться о заголовочном файле. Но если вы выберете не тот, он не скомпилируется, если заголовочный файл поставщика будет включен вместе с моим заголовочным файлом. Например, если это фактическое объявление VendorThing
в "vendor.h"
:
typedef struct { int a; int b; } VendorThing;
Тогда вы не можете просто объявить VendorThing
как class VendorThing;
. Меня вообще не волнует тип VendorThing, я хочу, чтобы публичный интерфейс воспринимал его как void *
(т.е. выделял место для указателя и все), а реализация думала об этом, используя правильный тип указателя.
Два решения, с которыми я столкнулся, - это метод "d-pointer", найденный в Qt, где вы добавляете уровень косвенности, заменяя VendorThing
новой структурой VendorThingWrapper
.
// myclass.h
struct VendorThingWrapper;
class MyClass
{
...
private:
VendorThingWrapper* vendorDataWrapper;
};
и в вашем файле cpp
// myclass.cpp
#include "vendor.h"
struct VendorThingWrapper {
VendorThing* vendorData;
};
... {
vendorDataWrapper->vendorData->doSomething();
}
но это добавляет второе разыменование указателя, что не имеет большого значения, но, поскольку это нацелено на встроенные системы, я не хочу добавлять эти накладные расходы только потому, что язык не может делать то, что я хочу.
Другое дело, просто объявить его недействительным
// myclass.h
class MyClass
{
...
private:
void* vendorDataUntyped;
};
и в реализации
//myclass.cpp
#include "vendor.h"
#define vendorData ((VendorThing*)vendorDataUntyped)
... {
vendorData->doSomething();
}
но #define всегда оставляет неприятный привкус во рту. Должно быть что-то лучше.
VendorThing
определено в заголовочном файле, то это не идиома pImpl... в pImpl она указывает на класс, который существует только в одном файле.cpp
. Это просто сдерживание VendorThing. Ваше первое предложение - это pImpl, и я думаю, что вы преувеличиваете проблемы двух уровней косвенности. Как насчет того, чтобыVendorData
имел типVendorThing
(не указатель)? - person M.M   schedule 27.07.2014vendorData
- person M.M   schedule 27.07.2014VendorThing vendorData;
частной функцией-членом, то мне нужно поместить#include "vendor.h"
в myclass.h, что противоречит всей цели сделать мой заголовок независимым от поставщика. - person Mark Lakata   schedule 27.07.2014VendorThing
была функцией. Но это в любом случае не имеет значения, вам не нужно#include "vendor.h"
в myclass.h. myclass.h делает#include "myclass.cpp"
, а myclass.cpp делает#include "vendor.h"
. ИмяVendorThing
должно появляться только в пределахmyclass.cpp
. - person M.M   schedule 27.07.2014VendorThing
, но имели в виду второе упоминание этого слова, а не первое. Да, теперь я понимаю. Ваше предложение совпадает с ответом jxh ниже. - person Mark Lakata   schedule 27.07.2014