инициализация класса
От: ma3ai  
Дата: 19.09.02 08:24
Оценка:
Доброго дня,

Имеем dll с функцией (для примера):
long _stdcall ff(char *value);

Так-же имеется класс который может инициализироваться:
cobj::cobj(char *value);

Если делать так то всё нормально:
long _stdcall ff(char *value)
{
    cobj xx = value; //вызывается cobj::cobj(char *value)
    ...
    return 0;
}

А вот так:
long _stdcall ff(cobj value) // cobj::cobj(char *value) не вызывается. Да что там - вообще ничего не вызывается.
                             // Просто в приватные переменные записывается что попало.
{
    ...
    return 0;
}


Где собака покопалась ? А может это в принципе неправильно или есть способ борьбы ?
... можно конечно использовать первый способ и не ломать голову, НО интересно же, да и красивше

Заранее спасибо за помощь.
...
Re: инициализация класса
От: Кирпа В.А. Украина  
Дата: 19.09.02 08:45
Оценка:
Здравствуйте ma3ai, Вы писали:

M>Доброго дня,


M>Имеем dll с функцией (для примера):

M>
M>long _stdcall ff(char *value);
M>

M>Так-же имеется класс который может инициализироваться:
M>
M>cobj::cobj(char *value);
M>

M>Если делать так то всё нормально:
M>
M>long _stdcall ff(char *value)
M>{
M>    cobj xx = value; //вызывается cobj::cobj(char *value)
M>    ...
M>    return 0;
M>}
M>

M>А вот так:
M>
M>long _stdcall ff(cobj value) // cobj::cobj(char *value) не вызывается. Да что там - вообще ничего не вызывается.
M>                             // Просто в приватные переменные записывается что попало.
M>{
M>    ...
M>    return 0;
M>}
M>


M>Где собака покопалась ? А может это в принципе неправильно или есть способ борьбы ?

M>... можно конечно использовать первый способ и не ломать голову, НО интересно же, да и красивше ;)

M>Заранее спасибо за помощь.


В классе cobj не хватает перегруженого оператора (char *) потому и не вызывается
!0xDEAD
Re[2]: инициализация класса
От: ma3ai  
Дата: 19.09.02 08:58
Оценка:
Здравствуйте Кирпа В.А., Вы писали:

КВА>В классе cobj не хватает перегруженого оператора (char *) потому и не вызывается


Спасибо за скорость. Попробую.
Только непонятно — зачем, если есть перегруженный способ инициализации ?
cobj::cobj()
cobj::cobj(char *)
cobj::cobj(...)
Или это — частный случай ? Хочется знать причину а не только решение
...
Re: инициализация класса
От: Анатолий СССР  
Дата: 19.09.02 09:06
Оценка:
Требуется дополнительная информация. В частности

M>long _stdcall ff(cobj value) // cobj::cobj(char *value) не вызывается.


А функция с такой сигнатурой у Вас есть, ведь в самом начале поста в качестве исходных данных Вы привели:
M>Имеем dll с функцией (для примера):
M>
M>long _stdcall ff(char *value);
M>


Проверьте те ли Вы функции эспортируете.
Re[2]: инициализация класса
От: ma3ai  
Дата: 19.09.02 09:14
Оценка:
Здравствуйте Анатолий, Вы писали:

А>Требуется дополнительная информация. В частности


M>>long _stdcall ff(cobj value) // cobj::cobj(char *value) не вызывается.


А>А функция с такой сигнатурой у Вас есть, ведь в самом начале поста в качестве исходных данных Вы привели:

M>>Имеем dll с функцией (для примера):
M>>
M>>long _stdcall ff(char *value);
M>>


А>Проверьте те ли Вы функции эспортируете.


ff(..) как раз и экспортируется ...
С вызовом проблем нет.
...
Re[3]: инициализация класса
От: Кирпа В.А. Украина  
Дата: 19.09.02 09:25
Оценка: 6 (1)
Здравствуйте ma3ai, Вы писали:

M>Здравствуйте Кирпа В.А., Вы писали:


КВА>>В классе cobj не хватает перегруженого оператора (char *) потому и не вызывается


M>Спасибо за скорость. Попробую.

M>Только непонятно — зачем, если есть перегруженный способ инициализации ?
M>cobj::cobj()
M>cobj::cobj(char *)
M>cobj::cobj(...)
M>Или это — частный случай ? Хочется знать причину а не только решение :???:

Компилятору надо вызвать функцию у которой параметр char *

Ты передаешь параметр клас cobj

Что ему делать?

Насколько я понял у тебя класс cobj содержит приват переменную типа char *m_str;

Вот если бы у тебя в хедере класса cobj стоял перегруженый оператор приведеения к типу char * то компилятор успокоился бы




inline operator char* () { return m_str; }


Еще правильней бы было чтобы твоя ф-ция принимала параметром const char * (если ета строка там не меняется)

и тогда оператор выглядел бы так


inline operator const char* () const { return m_str; }
!0xDEAD
Re[4]: инициализация класса
От: ma3ai  
Дата: 19.09.02 09:38
Оценка:
Здравствуйте Кирпа В.А., Вы писали:

КВА>Насколько я понял у тебя класс cobj содержит приват переменную типа char *m_str;


Точно.

КВА>Вот если бы у тебя в хедере класса cobj стоял перегруженый оператор приведеения к типу char * то компилятор успокоился бы


Спасибо, теперь всё понятно.
...
Re: инициализация класса
От: Sinclair Россия https://github.com/evilguest/
Дата: 19.09.02 09:47
Оценка:
Здравствуйте ma3ai, Вы писали:

M>Доброго дня,


M>Имеем dll с функцией (для примера):

M>
M>long _stdcall ff(char *value);
M>

M>Так-же имеется класс который может инициализироваться:
M>
M>cobj::cobj(char *value);
M>

M>Если делать так то всё нормально:
M>
M>long _stdcall ff(char *value)
M>{
M>    cobj xx = value; //вызывается cobj::cobj(char *value)
M>    ...
M>    return 0;
M>}
M>

M>А вот так:
M>
M>long _stdcall ff(cobj value) // cobj::cobj(char *value) не вызывается. Да что там - вообще ничего не вызывается.
M>                             // Просто в приватные переменные записывается что попало.
M>{
M>    ...
M>    return 0;
M>}
M>


M>Где собака покопалась ? А может это в принципе неправильно или есть способ борьбы ?

M>... можно конечно использовать первый способ и не ломать голову, НО интересно же, да и красивше

M>Заранее спасибо за помощь.

Я правильно понимаю, что ты пытаешься, задекларировав функцию ff таким образом:
long _stdcall ff(cobj value) 
{    
        ...
    return 0;
}

вызывать ее таким образом:

ff("teststring");

и ожидаешь, что компилер подставит неявное преобразование const char* в cobj путем вызова однопараметрического конструктора?

В таком случае, прежде всего надо понять, какой код собирается исполняться в dll, а какой — в приложении.
Дело в том, что при такой декларации код конструктора cobj должен быть выполнен в приложении, т.к. он происходит до вызова ff. В первом примере это было не так, код конструктора вызывался в dll.

Что-то мне подсказывает, что наиболее вероятная причина косяка — version mismatch. т.е. в длл у функции одна сигнатура, а вызывается она с другой.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: инициализация класса
От: Анатолий СССР  
Дата: 19.09.02 09:50
Оценка:
Зайдем с другой стороны

// cobj.h
class cobj
{
public:
    cobj() : m_buffer(0) {}
    cobj(const char *value) : m_buffer(0)
    {
       if( value ) 
       {        
          m_buffer = strcpy(new char[strlen(value)+1], value);
       }
    }
    cobj(const cobj &obj) : m_buffer(0)
    {
       if( obj.m_buffer ) 
       {        
          m_buffer = strcpy(new char[strlen(obj.m_buffer)+1], obj.m_buffer);
       }
    }
    cobj& operator = (const cobj &obj)
    {
       if( &obj == this) return *this;
       delete [] m_buffer;
       m_buffer = 0;
       if( obj.m_buffer ) 
       {        
          m_buffer = strcpy(new char[strlen(obj.m_buffer)+1], obj.m_buffer);
       }
       return *this;
    }
    ~cobj()
    {
        if( m_buffer ) delete [] m_buffer;
    } 
private:
    char *m_buffer;
};
// ff.h
#include "cobj.h"
extern long ff(cobj value);

// ff.cpp
long ff(cobj value)
{
   ...
   return sizeof(value);   
}
// main.cpp
#include "ff.h"
int main()
{
   ff("test"); && и что даже так не вызывается cobj::cobj(const char *)?
   return 0;
}
Re[4]: инициализация класса
От: ma3ai  
Дата: 19.09.02 10:29
Оценка:
Здравствуйте Все, Вы писали :

Чувствую, что я не до конца описал что я от этого хочу добиться. Но хотелось спросить проще и не городить огород.
Так вот:
1. эта ф-ия(ии) будет(ут) вызываться из VB (если это что-то меняет)
2. просто задался вопросом "а собственно, почему бы и нет ?" не надо писать лишнее
cobj xx = value;

и сразу убить n зайцев написав ff(cobj xx) — (и короче, и IMHO симпатичнее) //тут всё и инициализируется

P.S. Если это тоже поможет: Реально функция принимает BSTR * оно же хранится в классе.
(Знаю, знаю — существует n-множество уже готовых классов для BSTR, но мы здесь собрались обсуждать не это см. выше)

Ещё раз спасибо за отзывчивость.
...
Re[5]: инициализация класса
От: ma3ai  
Дата: 19.09.02 11:35
Оценка:
Забыл добавить.
компилятор считает что всё это законно и даже не выдаёт никаких предупреждений или ошибок несовпадения типов (он ещё не знает что передадут)
Переданный из VB указатель просто записывается в подставляемый класс.
long _stdcall ff(cobj value)
{
   //здесь переменные value содержат всякую ерунду, т.е. value указывает непонятно куда
   return 0;
}
...
Re[4]: инициализация класса
От: ma3ai  
Дата: 19.09.02 11:48
Оценка:
Здравствуйте Анатолий, Вы писали:

int main()
{
   ff("test"); && и что даже так не вызывается cobj::cobj(const char *)?
   return 0;
}


Из cpp наверное будет. А вот из VB — попробую.
...
Re[6]: Грабли
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.02 13:28
Оценка:
Здравствуйте ma3ai, Вы писали:

M>Забыл добавить.

M>компилятор считает что всё это законно и даже не выдаёт никаких предупреждений или ошибок несовпадения типов (он ещё не знает что передадут)
M>Переданный из VB указатель просто записывается в подставляемый класс.
M>long _stdcall ff(cobj value)
M>{
M>   //здесь переменные value содержат всякую ерунду, т.е. value указывает непонятно куда
M>   return 0;
M>}


Грабли, на которые ты собираешься наступить, выглядят следующим образом:
1. VB передаст тебе твои параметры так, как ты описал ему. Т.е. BSTR. Он ни сном ни духом не знает о каком-то классе cobj.
2. VC, который будет осуществлять выход из функции, должен освободить стек вызова. Соответственно именно он вызовет деструктор этого класса. С разрушением строки, переданной через параметр, чего нельзя делать для [in] параметров.

Эта же проблема была (и рассматривалась в форуме COM) между Buider-ом и VC в связи с разной спецификацией параметров функции.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[7]: Грабли
От: ma3ai  
Дата: 19.09.02 13:54
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>1. VB передаст тебе твои параметры так, как ты описал ему. Т.е. BSTR. Он ни сном ни духом не знает о каком-то классе cobj.

Согласен, не знает. Но речь идёт об инициализации класса, которая не происходит.

Vi2>2. VC, который будет осуществлять выход из функции, должен освободить стек вызова. Соответственно именно он вызовет деструктор этого класса. С разрушением строки, переданной через параметр, чего нельзя делать для [in] параметров.

Мысль правильная, но вся соль в следующем:
1. VB передаёт BSTR * . Его то и хранит класс.
2. Обработка переданной строки происходит через Sys[Re]AllocString (& её товарищи), как и рекомендует МСДН (не буду вдаваться в подробности) Всё это безобразие я и прячу в класс чтобы каждый раз не вспоминать (объявлена, но не инициализирована; содержит данные или нет; надо/не надо SysFreeString; как передана по значению или по ссылке и.т.д)
3. Поэтому разрушения строки нет — есть освобождение указателя на BSTR которую всё ещё "держит" VB
4. Эта другая тема, но всё равно спасибо за предупреждение.

Или я не прав ?
...
Re[7]: Грабли (пример)
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.02 13:55
Оценка:
Вот тебе пример подобной эквилибристики.

В одном модуле
extern "C" HRESULT __stdcall f2s( /*[in]*/ BSTR pszCmdLine );

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine, int       nCmdShow)
{
    // TODO: Place code here.

    BSTR s = ::SysAllocString(L"xyz");
    f2s( s );
    ::SysFreeString(s);
    return 0; 
}

В другом модуле
extern "C" HRESULT __stdcall f2s( /*[in]*/ CComBSTR pszCmdLine )
{ 
    pszCmdLine.Detach(); // чтобы "обмануть" компилятор. А если забудешь?! Да не таком простом примере.
    return 0;
}

Хороший стиль, не правда ли?
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[8]: Требования к классу
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.02 14:07
Оценка:
Здравствуйте ma3ai, Вы писали:

M>Согласен, не знает. Но речь идёт об инициализации класса, которая не происходит.


Значит, про инициализацию забудь — её не будет.

M>Мысль правильная, но вся соль в следующем:

M>1. VB передаёт BSTR * . Его то и хранит класс.
M>2. Обработка переданной строки происходит через Sys[Re]AllocString (& её товарищи), как и рекомендует МСДН (не буду вдаваться в подробности) Всё это безобразие я и прячу в класс чтобы каждый раз не вспоминать (объявлена, но не инициализирована; содержит данные или нет; надо/не надо SysFreeString; как передана по значению или по ссылке и.т.д)
M>3. Поэтому разрушения строки нет — есть освобождение указателя на BSTR которую всё ещё "держит" VB
M>Или я не прав ?

Твой класс должен удовлетворять следующим условиям:

1. У него не должно быть данных, отличных от передаваемых. Функций — сколько угодно. Виртуальных — ни одной, чтобы не появились данные.
2. Эти данные должны быть валидными как при настоящей инициализации класса.
3. У него не должно быть деструктора, разрушающего внешние объекты, адрусуемые данными класса. Проще вообще его не иметь.

При таких условиях — ради бога.

Но проще — сделать локальную переменную, и не думать о вот таких указанных тонкостях.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[8]: Грабли (пример)
От: ma3ai  
Дата: 19.09.02 14:11
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>Хороший стиль, не правда ли?


Х-мм. Может быть и "да". Кто-то наверняка стал бы спорить.
Но я не спрашивал "люди, помогите побороть гадкие BSTR, plz, plz, help, help"
А с чего начался разговор вы узнаете посмотрев начало темы.
[Не зря я сначала утаил ДЛЯ чего это нужно. К этому моменту, похоже, первоначальный смысл утерян навсегда]
...
Re[9]: Ну почему же утерян?!
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.02 14:18
Оценка:
Здравствуйте ma3ai, Вы писали:

M>Х-мм. Может быть и "да". Кто-то наверняка стал бы спорить.

M>Но я не спрашивал "люди, помогите побороть гадкие BSTR, plz, plz, help, help"
M>А с чего начался разговор вы узнаете посмотрев начало темы.
M>[...К этому моменту, похоже, первоначальный смысл утерян навсегда]

Ну почему же утерян?!
В указанном примере конструктор CComBSTR::CComBSTR(LPCOLESTR pSrc) тоже не будет вызван просто потому, что он о нём не знает. Он же не телепат. А линковка функции пройдёт из-за extern "C" по имени _f2s@4, которое определено в другом модуле.

Я просто взял достаточно простой пример. Не более того. Он случайно совпал с BSTR.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[10]: Ну почему же утерян?!
От: ma3ai  
Дата: 19.09.02 14:34
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>Ну почему же утерян?!

Ну, погорячился я немного ... ничего личного

Vi2>В указанном примере конструктор CComBSTR::CComBSTR(LPCOLESTR pSrc) тоже не будет вызван просто потому, что он о нём не знает.

Надо будет проверить ... Но скорей всего так оно и есть. А жаль.
...
Re[11]: Ещё вариант
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.02 14:46
Оценка:
Здравствуйте ma3ai, Вы писали:

M>А жаль.


Вот такое можно сделать в предыдущем примере:
extern "C" HRESULT __stdcall f2s( BSTR pszCmdLine )
{ 
    CComBSTR& szCmdLine = *(CComBSTR*) &pszCmdLine;

...

    return 0;
}

И использовать szCmdLine как CComBSTR на всю катушку — никаких проблем (с учётом тех требований к классу, кроме деструктора — он вызываться при таком раскладе не будет).
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[12]: Ещё вариант
От: ma3ai  
Дата: 19.09.02 15:17
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>
Vi2>extern "C" HRESULT __stdcall f2s( BSTR pszCmdLine )
Vi2>{ 
Vi2>    CComBSTR& szCmdLine = *(CComBSTR*) &pszCmdLine;

Vi2>...

Vi2>    return 0;
Vi2>}
Vi2>

Vi2>И использовать szCmdLine как CComBSTR на всю катушку — никаких проблем (с учётом тех требований к классу, кроме деструктора — он вызываться при таком раскладе не будет).

Можно, но это всё уже делает мой класс.
Может я и изобрёл велосипед, но то что он проще — это точно. То что нужно — делает.
P.S. может это врождённое, но не люблю я эти монстрообразные порождения на все случаи жизни. Другими словами: для чего мне "шнуркозавязыватель, использующий революционные технологии в области атомного взаимодействия межмолекулярных соединений с автооткатом, системой аварийного питания и возможностью работы в полярных условиях. как опция поставляется развязыватель" ? Ну может быть пока не нужно...
...
Re[4]: инициализация класса
От: Андрей Тарасевич Беларусь  
Дата: 19.09.02 16:50
Оценка:
Здравствуйте Кирпа В.А., Вы писали:

КВА>>>В классе cobj не хватает перегруженого оператора (char *) потому и не вызывается


M>>Спасибо за скорость. Попробую.

M>>Только непонятно — зачем, если есть перегруженный способ инициализации ?
M>>cobj::cobj()
M>>cobj::cobj(char *)
M>>cobj::cobj(...)
M>>Или это — частный случай ? Хочется знать причину а не только решение

КВА>Компилятору надо вызвать функцию у которой параметр char *


КВА>Ты передаешь параметр клас cobj


КВА>Что ему делать?


Насколько я понял, человек пытается сделать как раз наоборот. У функции параметр типа 'cobj'. Он хочет вызывать ее с параметром типа 'char*'. Для этого никакой оператор приведения типа не нужен. Нужен конвертирующий конструктор. И он у него есть.
Best regards,
Андрей Тарасевич
Re[2]: инициализация класса
От: Аноним  
Дата: 20.09.02 05:37
Оценка:
Здравствуйте Sinclair, Вы писали:

S>Что-то мне подсказывает, что наиболее вероятная причина косяка — version mismatch. т.е. в длл у функции одна сигнатура, а вызывается она с другой.


Да это вообще неверный подход. Функции, экспортируемые DLL, должны принимать только обычные типы. В противном случае мы либо имеем сплошные проблемы, либо должны явно указать что наша DLL накладывает требования на то, какими должны быть ее пользователи, и встраивать ее в некий каркас (по типу MFC Extension DLL или VCL Packages). Остальные случаи — несопровождаемы.
Re[3]: инициализация класса
От: ma3ai  
Дата: 20.09.02 06:18
Оценка:
Здравствуйте Аноним, Вы писали:

... экспортируемые DLL, должны принимать только обычные типы. ...

Похоже что так оно и есть.
Невольно задумаешся — как всё-таки ограничен наш мир ...
Спасибо всем за участие.
...
Re[4]: инициализация класса
От: Andrew S Россия http://alchemy-lab.com
Дата: 20.09.02 14:48
Оценка:
Здравствуйте ma3ai, Вы писали:

На самом деле, все, как обычно, просто. Когда ты пробуешь вызывать ff из C++ непосредственно, он, видя определение функции, конструирует из строки объект cobj и передает его (или его копию) в функцию. Т.е. собственно конструирование объекта (вызов перегруженного конструктора) происходит в приложение-хостере, которое грузит DLL. После чего в стеке на входе функции появляется экземпляр объекта. А когда ты вызываешь эту функцию из VB — там ее определяешь, как ff(BSTR). Соотв, передается не экземпляр объекта (или ссылка на него), а просто указатель на строку. И в стеке на входе появляется указатель на строку. И что будет в этом случае — одному богу известно. Но что ничего хорошего — 100%. Говоря проще — объект в данном случае конструирует не DLL, а приложение-хостер. Сишное приложение это делает, т.к. видит 'правильный' тип, а VB видит BSTR — что и честно отдает.

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

А мир — мир, поверь, очень многообразен.

Успехов.



M>Здравствуйте Аноним, Вы писали:


M>... экспортируемые DLL, должны принимать только обычные типы. ...


M>Похоже что так оно и есть.

M>Невольно задумаешся — как всё-таки ограничен наш мир ...
M>
M>Спасибо всем за участие.
M>
http://www.rusyaz.ru/pr — стараемся писАть по-русски
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.