Почему этот код динамической загрузки библиотек работает с gcc?

Предыстория:

Я столкнулся с незавидной задачей переноса приложения C++ GNU/Linux на Windows. Одна из задач этого приложения - поиск общих библиотек по определенным путям, а затем динамическая загрузка классов из них с помощью вызовов posix dlopen() и dlsym(). У нас есть очень веская причина для такой загрузки, которую я не буду здесь рассматривать.

Проблема:

Чтобы динамически обнаружить символы, сгенерированные компилятором C++ с помощью dlsym() или GetProcAddress(), они должны быть распутаны с помощью блока связей extern "C". Например:

#include <list>
#include <string>

using std::list;
using std::string;

extern "C" {

    list<string> get_list()
    {
        list<string> myList;
        myList.push_back("list object");
        return myList;
    }

}

Этот код является вполне корректным C++ и компилируется и выполняется многочисленными компиляторами как в Linux, так и в Windows. Однако он не компилируется в MSVC, потому что "возвращаемый тип не является допустимым C". Обходной путь, который мы придумали, - изменить функцию, чтобы она возвращала указатель на список вместо объекта list:

#include <list>
#include <string>

using std::list;
using std::string;

extern "C" {

    list<string>* get_list()
    {
        list<string>* myList = new list<string>();
        myList->push_back("ptr to list");
        return myList;
    }

}

Я пытался найти оптимальное решение для загрузчика GNU/Linux, которое будет работать как с новыми функциями, так и со старым устаревшим прототипом функции или, по крайней мере, будет определять, когда встречается устаревшая функция, и выдавать предупреждение. Для наших пользователей было бы неприлично, если бы код просто отключался, когда они пытались использовать старую библиотеку. Моя первоначальная идея состояла в том, чтобы установить обработчик сигнала SIGSEGV во время вызова get_list (я знаю, что это некрасиво - я открыт для лучших идей). Поэтому, чтобы убедиться, что загрузка старой библиотеки приведет к сбоям там, где я предполагал, я запустил библиотеку, использующую старый прототип функции (возвращающую объект list), через новый код загрузки (который ожидает указатель на список), и, к моему удивлению, она сработала. У меня возникает вопрос: почему?

Приведенный ниже код загрузки работает с обоими прототипами функций, перечисленными выше. Я подтвердил, что он работает на Fedora 12, RedHat 5.5 и RedHawk 5.1 с использованием gcc версий 4.1.2 и 4.4.4. Скомпилируйте библиотеки с помощью g++ с параметрами -shared и -fPIC, а исполняемый файл нужно скомпоновать с dl (-ldl).

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <string>

using std::list;
using std::string;

int main(int argc, char **argv)
{
    void *handle;
    list<string>* (*getList)(void);
    char *error;

    handle = dlopen("library path", RTLD_LAZY);
    if (!handle)
    {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    dlerror();

    *(void **) (&getList) = dlsym(handle, "get_list");

    if ((error = dlerror()) != NULL)
    {
        printf("%s\n", error);
        exit(EXIT_FAILURE);
    }

    list<string>* libList = (*getList)();

    for(list<string>::iterator iter = libList->begin();
          iter != libList->end(); iter++)
    {
        printf("\t%s\n", iter->c_str());
    }

    dlclose(handle);

    exit(EXIT_SUCCESS);
}
7
задан bckohan 23 February 2011 в 21:20
поделиться