C++ приведение из void* в SomeClass*

Я работаю над библиотекой C++, которая будет динамически загружаться (dlopen, dlsym...) в программах на C++ и C в качестве плагина.

Программы на C++ будут использовать функции разрушителя из библиотеки для вызова конструктора и деструктора соответственно. Что-то вроде этого:

void *creator(void *instance) {
    return new MyClass();
}

void destroyer(void *instance) {
    MyClass *_instance = static_cast<MyClass*>(instance);
    delete _instance;
}

Проблема в том, что это не тот тип безопасности. Возможно ли быть безопасным здесь? (static_cast, dynamic_cast, reinterpret_cast...)

Это важно, так как я намереваюсь создать оболочку C для каждого метода MyClass. Это позволило бы мне загрузить эту библиотеку в C-программу (что-то вроде переноса DBus C, которое можно использовать со стилем C или стилем C++). Так что я бы сделал что-то вроде этого:

int MyClassAddInts(void *instance, int a, int b) {
    MyClass *_instance = static_cast<MyClass*>(instance);
    return _instance->addInts(a, b);
}

Пожалуйста, имейте в виду, что это всего лишь фиктивный пример.

Спасибо большое.

4 ответа

Решение

На самом деле, нет. Весь смысл прохождения через интерфейс C состоит в том, что вы удаляете всю зависимость от любого C++ ABI, поэтому нет способа сохранить информацию о типах через нее "естественным" способом.

Если это действительно важно для вас, вы могли бы настроить некоторую сложную инфраструктуру метаданных, чтобы учесть некоторые тесты во время выполнения, но вы наверняка не получите от этого безопасность типов во время компиляции.

Вы могли бы использовать std::map<void*, MyClass*>, Затем, если указатель не будет распознан, вместо неопределенного поведения вы получите ошибку типа "элемент не найден в карте".

Вы могли бы также рассмотреть использование ручек (не указатели - только целые). Например, вы можете хранить глобальный массив или карту, которая соответствует int с указателем, и заставить все ваши функции принимать только такие дескрипторы, а затем использовать этот глобальный массив или карту, чтобы найти соответствующий указатель. В случае массива дескриптор будет просто индексом в массиве.

Возможно, вы сможете получить некоторую меру безопасности типов, предоставив указатели C API на структуры в стиле C, которые, в свою очередь, содержат указатели на ваши объекты C++. Тогда у вас будет больше контроля над указателями (при условии, что клиенты C не вмешиваются в структуры, что они, безусловно, не должны), и есть некоторая проверка типов также на стороне C. Что-то вроде:

struct MyClassHandle {
    void * ptr;
}

Тогда у вас есть довольно хорошая идея, что ptr из MyClassHandle будет успешно приведен к MyClass *.

Но, как сказал Kerrek SB, вы не можете получить какую-либо безопасность типов, когда кастуете из void * - информации просто нет.

Другие вопросы по тегам