Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [VC++]импорт методов, и работа с их адресами 
:(
    Опции темы
GoldFinch
Дата 16.5.2009, 20:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Есть длл, написанная на VС++, экспортирующая методы классов (возможно сами классы). 
Заголовочных файлов к этой длл нет.

При помощи dumpbin.exe были получены экспорты длл, при помощи lib.exe был получен .lib файл, при помощи undname.exe были получены имена и прототипы методов, при помощи IDA был восстановлен порядок методов в таблицах виртуальных методов (ВМТ)

Тестовый код "UObject o; o.AddRef;" показал что метод UObject::AddRef() импортируется корректно и генерирует корректный код вызова
Код
 call    ?AddRef@UObject@@UAGKXZ
?AddRef@UObject@@UAGKXZ:
  jmp [__imp_?AddRef@UObject@@UAGKXZ]


Теперь надо получить адрес этого метода. 
Допустим надо получить адрес простой импортированной функции. Пишем 
  int foo(){return int(&ExitProcess);}
получаем 
  mov     eax, [__imp__ExitProcess@4]
Для метода, пробуем написать
  int foo(){return int(&UObject::AddRef);}
получаем error C2440: приведение типов: невозможно преобразовать 'unsigned long (__stdcall UObject::* )(void)' в 'int'  Не существует контекста, в котором такое преобразование возможно

Пишем шаблон для преобразования
Код

template<typename T>
inline int int_cast(T x){
    union {
        T x;
        int y;
    } t;
    t.x=x;    
    return t.y;
};

код
  int foo(){return int_cast(&UObject::AddRef);}
компилится успешно, однако получаем адрес функции которая вызывает метод через таблицу методов
Код
?foo@@YAHXZ proc near
mov     eax, offset ??_9UObject@@$B3AG ; [thunk]: UObject::`vcall'{4,{flat}}
retn
?foo@@YAHXZ endp
??_9UObject@@$B3AG proc near

arg_0= dword ptr  4

mov     eax, [esp+arg_0]
mov     eax, [eax]
jmp     dword ptr [eax+4] ;4 - смещение метода в ВМТ
??_9UObject@@$B3AG endp

а хочется совсем не этого, хочется получить реальный адрес этого метода, т.е. [__imp_?AddRef@UObject@@UAGKXZ]

как это сделать?

Это сообщение отредактировал(а) GoldFinch - 16.5.2009, 20:38
PM MAIL ICQ   Вверх
Earnest
Дата 16.5.2009, 21:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



А метод случаем не виртуальный? Когда ты просишь адрес виртуального метода, то тебе компилятор подсовывает адрес именно функции-переходника, т.к. предполоагается полиморфный вызов в зависимости от this. 
И еще один вопрос: неужели банальный GetProcAddress не поможет (с истинным декорированным именем, конечно)? Если функция импортируется, то GetProcAddress должна возвратить ее, а метод с точки зрения линкера - та же функция... С преобразованием типа тут тоже возможны проблемы, конечно... 


--------------------
...
PM   Вверх
GoldFinch
Дата 16.5.2009, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Earnest, метод виртуальный. сделать его невиртуальным невозможно, т.к. не будет соответствовать манглинг.

GetProcAddress я както использовал, но потом от этого отказался вроде изза большего объема кода
Код

//глобальные переменные
  typ1 (class1::*met1)(agrlist1);
  typ2 (class1::*met1)(agrlist2);
  ...
//импорт
  (FARPROC&)met1=GetProcAddress(hMod,"?met1?class1?...");
  (FARPROC&)met2=GetProcAddress(hMod,"?met2?class2?...");
  ...
//вызов
  r=(obj->*met)(arg1,arg2,...);

проблем с преобразованием типа нет, а вот вызов выглядит не очень красиво

Это сообщение отредактировал(а) GoldFinch - 16.5.2009, 22:00
PM MAIL ICQ   Вверх
Earnest
Дата 16.5.2009, 22:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Я и не предлагала сделать его невиртуальным, просто скзала, как компилятор интерпретирует такие вызовы (и заодно указатели). GetProcAddress - это стандартный метод получения адресов функций, чего тут мудрить. А если 
не нравится код, напиши обертку к импортируемым классам (чтобы для клиента выглядело все как у "нормальных").
И не совсем понимаю, каким образом твой первый метод решал эту проблему (некрасивого кода вызова).

Кстати, ты ведь вроде говорил, что смог построить lib-файл. Так что тогда мешает подключить билиотеку стандартным оразом? Ну, заголовочный файл напиши руками, ты ведь вроде выяснил сигнатуры... Насколько я понимаю, цель именно такая: использовать библиотеку как если бы у нее были открыт интерфейсы (т.е. lib + h)?


--------------------
...
PM   Вверх
GoldFinch
Дата 17.5.2009, 00:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Earnest, 
я уже писал в 1м посте, что вызовы методов работают нормально, obj->met(),
мне же надо получить адрес метода, не для того чтобы его вызвать, а для последующей работы с этим адресом.
PM MAIL ICQ   Вверх
Earnest
Дата 17.5.2009, 00:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Наверное, пропустила.
В общем, адрес метода - это совсем не адрес с точки зрения компилятора и языка, так что просто &CMyObj::Method будет возвращать адрес стаба, как у тебя и получается. А вот GetProcAddress про C++ и прочий полиморфизм не в курсе, должна выдавать ровно то, что просят. Насчет длинного и некрасивого кода - как писал кто-то из гуру, любую проблему в программировании можно решить с помощью правильно спроектированных прокладок. А на C++ они к тому же почти бесплатны в смысле издержек.


--------------------
...
PM   Вверх
GoldFinch
Дата 17.5.2009, 00:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



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


Если же задуматься об издержках на импорт, то получается что при статическом импорте компилятор удаляет все неиспользуемые импорты и они импортируются поиском в таблице экспорта длл при загрузке программы. При вызове метода через указатель на объект (obj->met) адрес метода берется из ВМТ.
При динамическом импорте через GetProcAddress импортирование происходит поиском в таблице экспорта длл в момент вызова GetProcAddres. Будут ли удалены лишние вызовы GetProcAddress - неизвестно. При вызове метода адрес метода берется из переменной с адресом.
Однако у меня конкретная длл, которая не будет меняться за все время использования моей программы. Поэтому для импорта можно забить готовые адреса методов сразу на этапе компиляции, они не изменятся. При этом получится наибольшая скорость вызова функций, хотя не очень понятно как это будет выглядеть в коде, и как генерировать этот код из длл.


PM MAIL ICQ   Вверх
mes
Дата 17.5.2009, 10:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  16.5.2009,  19:21 Найти цитируемый пост)

Тестовый код "UObject o; o.AddRef;" показал что метод UObject::AddRef() импортируется корректно и генерирует корректный код вызова
Код


 call    ?AddRef@UObject@@UAGKXZ
?AddRef@UObject@@UAGKXZ:
  jmp [__imp_?AddRef@UObject@@UAGKXZ]




Код

void test (UObject& o) {    o.AddRef();  } 

а такой тестовый код, что дает ?


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 17.5.2009, 13:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



mes, вызов через ВМТ
PM MAIL ICQ   Вверх
mes
Дата 17.5.2009, 13:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  17.5.2009,  12:11 Найти цитируемый пост)
вызов через ВМТ 

тогда то, что Вы получили в первом примере, относится к оптимизации и получено вследствие того, что компилятору известен реальный тип объекта в компил-тайм.
И задача (без знания реального имени функции-реализации), имхо, не выполнима  


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 19.6.2009, 21:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



ап!

Цитата(Earnest @  17.5.2009,  01:12 Найти цитируемый пост)
как писал кто-то из гуру, любую проблему в программировании можно решить с помощью правильно спроектированных прокладок.


попробовал написать обертку
Код

//=============== .h =====================

class Foo;

class Foo_Imp_
{
protected:
    static void (Foo::*Met1_ptr)(int);
    static int (Foo::*Met2_ptr)();
    static int (Foo::*Met3_ptr)(int,char);
    // ... еще методы ...
protected:
    Foo_Imp_()
    {
        if (!imported_)
        {
            imported_=true;
            HMODULE hDll=TheDll::Handle();  //загружает и возвращает хендл длл "the.dll"
            (FARPROC&)Met1_ptr=GetProcAddress(hDll,"??Met1@@Foo");
            (FARPROC&)Met2_ptr=GetProcAddress(hDll,"??Met2@@Foo");
            (FARPROC&)Met3_ptr=GetProcAddress(hDll,"??Met3@@Foo");
            // ... еще методы ...
        }
    }
private:
    static bool imported_;
};


class Foo :Foo_Imp_
{
public:
    char** VMT; //ВМТ класса
    int thedata1; //какието известные члены класса
    char stuff[0x34]; //какието неизвестные члены класса
public:
    void Met1(int arg1) {return (this->*Foo_Imp_::Met1_ptr)(arg1);}
    int Met2() {return (this->*Foo_Imp_::Met2_ptr)();}
    int Met3(int arg1,char arg2) {return (this->*Foo_Imp_::Met3_ptr)(arg1,arg2);}
    // ... еще методы ...
};


//================ .cpp =================

bool Foo_Imp_::imported_=false;

void (Foo::*Foo_Imp_::Met1_ptr)(int);
int (Foo::*Foo_Imp_::Met2_ptr)();
int (Foo::*Foo_Imp_::Met3_ptr)(int,char);
// ... еще методы ...



есть какие-нибудь идеи как сделать это короче?

Это сообщение отредактировал(а) GoldFinch - 19.6.2009, 21:18
PM MAIL ICQ   Вверх
Earnest
Дата 22.6.2009, 09:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



GoldFinch, а зачем? Ты код буста видел? Цель в том, чтобы был короче-яснее клиентский код, а обслуживающий... ну, по возможности.
Насчет сократить, я бы попробовала так (имея в виду, что методов сильно не 3).
Во-первых, таблица вместо перечисления методов, причем не только имя, но и закодированная сигнатура.
Во-вторых, набор типизированных шаблонов для вызова методов по сигнатуре. Шаблоны нужно делать по числу аргументов: для 0, 1, 2, 3 и сколько там по максимуму.
Методы имлементировать придется явно, как у и у тебя, но через шаблоны.
И еще не поняла, зачем тебе в обертке имитировать тело настоящего класса, все эти VMT, члены-данные... Т.е. если ты можешь правильно установить VMT, но нафига вообще огород городить - напиши объявление класса с правильно расположенными виртуальными функциями.


--------------------
...
PM   Вверх
GoldFinch
Дата 22.6.2009, 13:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Earnest, обертка чтобы спрятать указатели на методы, и она превращает виртуальные методы в длл в невиртуальные, это позволяет вызывать их не зная их порядок в ВМТ (хотя они в оригинале виртуальные, полиморфизм там вроде не используется)

количество методов в 1 классе - до сотен (200-300), классы и длл писал не я
код буста не видел, но мне надо будет скорее всего писать все это руками, по мере необходимости добавляя новые методы 

PM MAIL ICQ   Вверх
mes
Дата 22.6.2009, 13:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  19.6.2009,  20:18 Найти цитируемый пост)
попробовал написать обертку

с певого взгляда бросилось в глаза

Цитата(GoldFinch @  19.6.2009,  20:18 Найти цитируемый пост)
Код

class Foo_Imp_
{
protected:
    static void (Foo::*Met1_ptr)(int);
    static int (Foo::*Met2ptr)();  

Зачем сразу ограничивать себя, принуждая дата-члены класса делать статическими.
Этот момент интуитивно напрашивается в таком исполнении :
Код

class Foo;
struct PtrTable
{
    void (Foo::*Met1)(int);
    int  (Foo::*Met2)();

    PtrTable () { /* import */ }

};

class Foo
{
    public:
      void  Met1 (int i) {  return (this->*table().Met1) (i); }
      int   Met2 ()      {  return (this->*table().Met2) ();  }

    private:
        static PtrTable& table() { static PtrTable table; return table; } // 1.
};


1. Выполнил как функцию, так как таблица принадлежит только этому классу, но если не подходит можно вернуть к "классическому" виду
 инициализации таблица произойдет при первом вызове функции PtrTable. Если нужно ручное управление инициализацией, то можно добавить функцию:
static void Init () { PtrTable(); } или поместить пустой вызов PtrTable() в конструктор в зависимости от требований.
ну а красиво  скрыть это : (this->*table().Met1)  я уверен Вы справитесь сами smile






--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 17.7.2009, 12:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



ап!

почитал про static, действительно оно обладает замечательным свойством инициализировать переменные только 1 раз

получается что 
отдельный класс PtrTable и "static PtrTable& table() { static PtrTable table; return table; }"
можно заменить на статическую переменную с адесом метода прямо внутри обертки метода
при этом эту переменную можно сделать классом, в конструкторе которого и импортировать метод

Код

// обертка winapi
class Dll
{
public:
    template<typename T>
    static void import(T& f, const char* name)
    {
        if( !handle_ )
            handle_ = LoadLibraryA("test_dll.dll");
        (FARPROC&)f=GetProcAddress(handle_,name);
    }
private:
    static HMODULE handle_;
};
HMODULE Dll::handle_ = 0;

Код

// импортируемый класс
class Foo
{
...

    void init(char* str) 
    { 
        static struct Imp_
        {
            void (Foo::*met)(char* str);
            Imp_() { Dll::import(met,"?init@Foo@@UAEXPAD@Z"); }            
        } imp_;
        return (this->*imp_.met)(str); 
    }

...
};


получается что вся информация о методе сосредоточена в одном месте, и ее можно завернуть в компактный макрос
PM MAIL ICQ   Вверх
GoldFinch
Дата 17.7.2009, 12:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



а еще лучше выкинуть класс Imp_ из метода:
Код

struct Imp_
{
    void* addr;
    Imp_(const char* name) { Dll::import(addr,name); }            

    template<typename T>
    T call_(T proto)
    {
        BOOST_STATIC_ASSERT(sizeof(proto)==sizeof(addr));
        union U
        {
            void* x;
            T y;
        } u = {addr};
        return u.y;
    }
};

Код

class Foo
{
public:
...
    void init(char* str) 
    { 
        static Imp_ imp_("?init@Foo@@UAEXPAD@Z");
        return (this->*imp_.call_(&Foo::init))(str); 
    }
...
}


Добавлено через 14 минут и 30 секунд
хм... только вот не получается получить адрес конструктора и деструктора =\
PM MAIL ICQ   Вверх
GoldFinch
Дата 17.7.2009, 13:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



значит всетаки так:
Код

template<typename T>
struct Imp_
{
    T addr;
    Imp_(const char* name) { Dll::import(addr,name); }            
};

class Foo
{
public:
    int* vftable;
    char* str;
public:    
    Foo()
    {
        static Imp_<void(Foo::*)()> imp_("??0Foo@@QAE@XZ");
        (this->*imp_.addr)(); 

        Dll::import(vftable,"??1Foo@@UAE@XZ");
    }
    ~Foo() 
    {
        static Imp_<void(Foo::*)()> imp_("??0Foo@@QAE@XZ");
        (this->*imp_.addr)();
    }

    void init(char* str) 
    { 
        static Imp_<void(Foo::*)(char*)> imp_("?init@Foo@@UAEXPAD@Z");
        return (this->*imp_.addr)(str); 
    }

    void test() 
    { 
        static Imp_<void(Foo::*)()> imp_("?test@Foo@@UAEXXZ");
        return (this->*imp_.addr)(); 
    }    

};

PM MAIL ICQ   Вверх
mes
Дата 17.7.2009, 14:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  17.7.2009,  12:19 Найти цитируемый пост)
   return (this->*imp_.addr)(); 

добавьте в структуру Imp_ :   
Код

 operator T () { return addr; } 

и отпадет надобность писать .addr
 smile 


Это сообщение отредактировал(а) mes - 17.7.2009, 14:38


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 17.7.2009, 16:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



mes, я решил пойти еще дальше, и написать эту структуру так:
Код

namespace import {

    ...

    template<typename T>
    T inline union_cast(void* x)
    {
        BOOST_STATIC_ASSERT(sizeof(T)==sizeof(x));
        union U
        {
            void* x;
            T y;
        } u = {x};
        return u.y;
    }

    template<typename retT=void>
    struct Import
    {
        void* addr;
        Import(const char* name) { Dll::import(addr,name); }

        template<typename classT>
        retT operator() (classT* that) 
        { return (that->*union_cast<retT(classT::*)()>(addr))();    }

        template<typename classT, typename T1>
        retT operator() (classT* that, T1 arg1) 
        { return (that->*union_cast<retT(classT::*)(T1)>(addr))(arg1); }

        template<typename classT, typename T1, typename T2>
        retT operator() (classT* that, T1 arg1, T2 arg2) 
        { return (that->*union_cast<retT(classT::*)(T1,T2)>(addr))(arg1,arg2); } 
        
        ...
    };

} // namespace import

тогда ее можно использовать так:
Код

class Foo
{
    ...

    int sum(int a,char b)
    {
        static import::Import<int> imp_("?sum@Foo@@QAEHHD@Z");
        return imp_(this,a,b); 
    }
};

PM MAIL ICQ   Вверх
GoldFinch
Дата 17.7.2009, 17:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



объявляю эту тему форума репозиторием версий кода импорта методов =)

перенес аргумент шаблона retT из класса в operator()
Код

    template<typename T=void> struct ret_tag{};

    struct Import
    {
        void* addr;
        Import(const char* name) { Dll::import(addr,name); }

        template<typename retT,typename classT>
        retT operator() (ret_tag<retT>,classT* that) 
        { return (that->*union_cast<retT(classT::*)()>(addr))();    }

        template<typename retT,typename classT, typename T1>
        retT operator() (ret_tag<retT>,classT* that, T1 arg1) 
        { return (that->*union_cast<retT(classT::*)(T1)>(addr))(arg1); }

        template<typename retT,typename classT, typename T1, typename T2>
        retT operator() (ret_tag<retT>,classT* that, T1 arg1, T2 arg2) 
        { return (that->*union_cast<retT(classT::*)(T1,T2)>(addr))(arg1,arg2); } 

    };

// ************************* использование *****************
class Foo
{
    ...

    int sum(int a,char b)
    {
        static import::Import imp_("?sum@Foo@@QAEHHD@Z");
        return imp_(import::ret_tag<int>(),this,a,b); 
    }
};


в стл или бусте есть класс 
template<typename T> struct какое_нибудь_имя{}; ?

Добавлено через 7 минут и 15 секунд
и есть ли гденить в бусте (в стл нет) аналог такой функции?
Код

    template<typename yT,typename xT>
    yT inline union_cast(xT x)
    {
        BOOST_STATIC_ASSERT(sizeof(xT)==sizeof(yT));
        union U
        {
            xT x;
            yT y;
        } u = {x};
        return u.y;
    }


(нужна для приведения указателей на члены классов, т.к. они не приводятся приведеним типов в С-стиле)
PM MAIL ICQ   Вверх
mes
Дата 17.7.2009, 20:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  17.7.2009,  15:33 Найти цитируемый пост)
решил пойти еще дальше, и написать эту структуру так:

Цитата(GoldFinch @  17.7.2009,  15:33 Найти цитируемый пост)
      union U
        {
            void* x;
            T y;
        } u = {x};
        return u.y;


Mне кажется Вас опять относит в сторону - снова перекладываете работу по контролю типов с компилятора на свои плечи...


Это сообщение отредактировал(а) mes - 17.7.2009, 20:23


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 18.7.2009, 10:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



mes, если компилятор не умеет присваивать такие указатели, мне приходится делать это за него.


переписал с использованием boost.preprocessor :

Код

//import.h

...

#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/preprocessor/array/size.hpp>
#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/if.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>

#define IMPORT_IMPL_ARG_DECL_(z, i, data)    \
    BOOST_PP_COMMA_IF(i)                    \
    BOOST_PP_ARRAY_ELEM(i,data)                \
    BOOST_PP_CAT(arg,i)                        \
    /*--*/

#define IMPORT_IMPL_ARGLIST_DECL_(a_arg)    \
    BOOST_PP_REPEAT(                        \
        BOOST_PP_ARRAY_SIZE(a_arg),            \
        IMPORT_IMPL_ARG_DECL_,                \
        a_arg)                                \
    /*--*/

#define IMPORT_IMPL_EMPTY_1_(x)     /*nothing*/

#define IMPORT_IMPL_SIGNATURE_(name,arg_t,arg_n)    \
    name(                                            \
        BOOST_PP_IF(arg_n,                            \
            IMPORT_IMPL_ARGLIST_DECL_,                \
            IMPORT_IMPL_EMPTY_1_                    \
        )((arg_n,arg_t))                            \
    )                                                \
    /*--*/

#define IMPORT_IMPL_ARGLIST_(arg_n)        \
    BOOST_PP_COMMA_IF(arg_n)            \
    BOOST_PP_ENUM_PARAMS( arg_n, arg)    \
    /*--*/

#define IMPORT_METHOD(ret_t,name,arg_t,arg_n,exp_name)    \
    ret_t IMPORT_IMPL_SIGNATURE_(name,arg_t,arg_n)        \
    {                                                    \
        static import::Import imp_(exp_name);            \
        return imp_(import::ret_tag<ret_t>(),this        \
            IMPORT_IMPL_ARGLIST_(arg_n)    );                \
    }                                                    \
    /*--*/

#define IMPORT_CONSTRUCTOR(clsname,arg_t,arg_n,exp_name,vft_name)    \
    IMPORT_IMPL_SIGNATURE_(clsname,arg_t,arg_n)                        \
    {                                                                \
        import::Dll::import(vftable,vft_name);                        \
        static import::Import imp_(exp_name);                        \
        imp_(import::ret_tag<>(),this                                \
            IMPORT_IMPL_ARGLIST_(arg_n)    );                            \
    }                                                                \
    /*--*/

#define IMPORT_DESTRUCTOR(clsname,exp_name)        \
    ~clsname()                                    \
    {                                            \
        static import::Import imp_(exp_name);    \
        imp_(import::ret_tag<>(),this);            \
    }                                            \
    /*--*/

...

Код

// foo.h

#ifndef Foo_h__
#define Foo_h__

#include "Import.h"

class Foo
{
public:
    int* vftable;
    char* str;
public:    
    IMPORT_CONSTRUCTOR( Foo, (),0, "??0Foo@@QAE@XZ", "??1Foo@@UAE@XZ")
    IMPORT_DESTRUCTOR( Foo, "??1Foo@@UAE@XZ")

    IMPORT_METHOD(void,    init,    (char*),1,        "?init@Foo@@UAEXPAD@Z")
    IMPORT_METHOD(int,    sum,    (int,char),2,    "?sum@Foo@@QAEHHD@Z")
    IMPORT_METHOD(void,    test,    (),0,            "?test@Foo@@UAEXXZ")
};

#endif // Foo_h__


оригинальный класс:
Код

class __declspec(dllexport) Foo {
public:
    Foo();

    virtual void init(char* str);
    virtual void test();

    int sum(int a, char b); 

    virtual ~Foo();

private:
    char* str_;
};


Добавлено через 48 секунд
косушки расползлись =\
PM MAIL ICQ   Вверх
mes
Дата 18.7.2009, 14:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  17.7.2009,  16:17 Найти цитируемый пост)

(нужна для приведения указателей на члены классов, т.к. они не приводятся приведеним типов в С-стиле) 

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

Вобщем я так вижу
Код

class Foo 
{
 public: 
      void f (int m)  { static Imp<void (*)(Foo*, int )> imp; imp(this, m); }
}

Сейчас времени больше нет, приду если что не понятно поподробней опишу.



--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 18.7.2009, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



mes, там __thiscall, this передается в ecx
PM MAIL ICQ   Вверх
mes
Дата 18.7.2009, 20:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  18.7.2009,  18:07 Найти цитируемый пост)
там __thiscall, this передается в ecx 

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





--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 18.7.2009, 20:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



mes, так и есть, нужная мне длл, и мой код компилятся одним компилятором
PM MAIL ICQ   Вверх
mes
Дата 18.7.2009, 20:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  18.7.2009,  19:21 Найти цитируемый пост)
так и есть, нужная мне длл, и мой код компилятся одним компилятором 

в таком случае самым простым способом будет все таки union... (и программист как гарант типо-преобразований)
мне кажется только сам union было бы удобно спрятать в Вашем адаптере (Imp_) :
Код

template<typename T>
struct ThiscallImport_
{   
      union
      {
            T       mtd;
            void*   proc;             
       } addr;

       operator T () { return addr.mtd; } 

      ThiscallImport_(const char* name) { Dll::import(addr.proc,name); }            
};

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


Это сообщение отредактировал(а) mes - 18.7.2009, 20:52


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 6.8.2009, 14:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



ап.
практически всю информацию о методе можно выдрать из его типа.
в msvc есть интринсик _AddressOfReturnAddress() возвращающий указатель на фрейм стека

поэтому возможен следующий код
Код

class Foo
{
...

    void init(char* some_arg_name)
    {        
        static import::Import2 imp_("?init@Foo@@UAEXPAD@Z");
        return imp_( _AddressOfReturnAddress(), this, &Foo::init );
    }

где
Код

namespace import {
    struct Import2
    {
        void* addr;
        Import2(const char* name) { Dll::import(addr,name); }

        template<typename retT, typename clsT, typename T1>
        retT operator() ( void* frame, clsT* that, retT (clsT::*ptr)(T1) )
        {
#pragma pack(push,4)
            struct StackFrame
            {
                char* ret;
                T1 a1;
            } *f = (StackFrame*)frame;
#pragma pack(pop)
            return (that->*union_cast<retT(clsT::*)(T1)>(addr))(f->a1);
        }

    };
} // namespace import


Добавлено через 9 минут и 56 секунд
с макросами это будет
Код

#define IMPORT_METHOD_2(retT,name,args,exp_name)                \
    retT name args                                                \
    {                                                            \
        static import::Import2 imp_(exp_name);                    \
        return imp_( _AddressOfReturnAddress(), this, &name );    \
    }

// void Foo::init(char*);
IMPORT_METHOD_2( void, Foo::init, (char* some_arg_name), "?init@Foo@@UAEXPAD@Z" )


вместо 
Код

IMPORT_METHOD(void,    init,    (char*),1,        "?init@Foo@@UAEXPAD@Z")

и никакого BOOST_PP

Это сообщение отредактировал(а) GoldFinch - 6.8.2009, 14:30
PM MAIL ICQ   Вверх
GoldFinch
Дата 7.8.2009, 14:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



а вот адрес конструктора получить нельзя =\

Добавлено @ 14:49
другое дело, что конструктор можно объявить как обычный метод, и вызывать его явно.

а с деструктором проблем нет, т.к. его сигнатура всегда известна

Это сообщение отредактировал(а) GoldFinch - 7.8.2009, 14:52
PM MAIL ICQ   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




[ Время генерации скрипта: 0.4874 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.