Здравствуйте 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 * (если ета строка там не меняется)
Так-же имеется класс который может инициализироваться:
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;
}
Где собака покопалась ? А может это в принципе неправильно или есть способ борьбы ?
... можно конечно использовать первый способ и не ломать голову, НО интересно же, да и красивше
M>long _stdcall ff(cobj value) // cobj::cobj(char *value) не вызывается. Да что там - вообще ничего не вызывается.
M> // Просто в приватные переменные записывается что попало.
M>{
M> ...
M> return 0;
M>}
M>
M>Где собака покопалась ? А может это в принципе неправильно или есть способ борьбы ? M>... можно конечно использовать первый способ и не ломать голову, НО интересно же, да и красивше ;)
M>Заранее спасибо за помощь.
В классе cobj не хватает перегруженого оператора (char *) потому и не вызывается
Здравствуйте Кирпа В.А., Вы писали:
КВА>В классе cobj не хватает перегруженого оператора (char *) потому и не вызывается
Спасибо за скорость. Попробую.
Только непонятно — зачем, если есть перегруженный способ инициализации ?
cobj::cobj()
cobj::cobj(char *)
cobj::cobj(...)
Или это — частный случай ? Хочется знать причину а не только решение
Здравствуйте Анатолий, Вы писали:
А>Требуется дополнительная информация. В частности
M>>long _stdcall ff(cobj value) // cobj::cobj(char *value) не вызывается.
А>А функция с такой сигнатурой у Вас есть, ведь в самом начале поста в качестве исходных данных Вы привели: M>>Имеем dll с функцией (для примера): M>>
M>>long _stdcall ff(char *value);
M>>
А>Проверьте те ли Вы функции эспортируете.
ff(..) как раз и экспортируется ...
С вызовом проблем нет.
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. т.е. в длл у функции одна сигнатура, а вызывается она с другой.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Чувствую, что я не до конца описал что я от этого хочу добиться. Но хотелось спросить проще и не городить огород.
Так вот:
1. эта ф-ия(ии) будет(ут) вызываться из VB (если это что-то меняет)
2. просто задался вопросом "а собственно, почему бы и нет ?" не надо писать лишнее
cobj xx = value;
и сразу убить n зайцев написав ff(cobj xx) — (и короче, и IMHO симпатичнее) //тут всё и инициализируется
P.S. Если это тоже поможет: Реально функция принимает BSTR * оно же хранится в классе.
(Знаю, знаю — существует n-множество уже готовых классов для BSTR, но мы здесь собрались обсуждать не это см. выше)
Забыл добавить.
компилятор считает что всё это законно и даже не выдаёт никаких предупреждений или ошибок несовпадения типов (он ещё не знает что передадут)
Переданный из VB указатель просто записывается в подставляемый класс.
long _stdcall ff(cobj value)
{
//здесь переменные value содержат всякую ерунду, т.е. value указывает непонятно кудаreturn 0;
}
Здравствуйте 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 в связи с разной спецификацией параметров функции.
Здравствуйте Vi2, Вы писали:
Vi2>1. VB передаст тебе твои параметры так, как ты описал ему. Т.е. BSTR. Он ни сном ни духом не знает о каком-то классе cobj.
Согласен, не знает. Но речь идёт об инициализации класса, которая не происходит.
Vi2>2. VC, который будет осуществлять выход из функции, должен освободить стек вызова. Соответственно именно он вызовет деструктор этого класса. С разрушением строки, переданной через параметр, чего нельзя делать для [in] параметров.
Мысль правильная, но вся соль в следующем:
1. VB передаёт BSTR * . Его то и хранит класс.
2. Обработка переданной строки происходит через Sys[Re]AllocString (& её товарищи), как и рекомендует МСДН (не буду вдаваться в подробности) Всё это безобразие я и прячу в класс чтобы каждый раз не вспоминать (объявлена, но не инициализирована; содержит данные или нет; надо/не надо SysFreeString; как передана по значению или по ссылке и.т.д)
3. Поэтому разрушения строки нет — есть освобождение указателя на BSTR которую всё ещё "держит" VB
4. Эта другая тема, но всё равно спасибо за предупреждение.
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;
}
Здравствуйте ma3ai, Вы писали:
M>Согласен, не знает. Но речь идёт об инициализации класса, которая не происходит.
Значит, про инициализацию забудь — её не будет.
M>Мысль правильная, но вся соль в следующем: M>1. VB передаёт BSTR * . Его то и хранит класс. M>2. Обработка переданной строки происходит через Sys[Re]AllocString (& её товарищи), как и рекомендует МСДН (не буду вдаваться в подробности) Всё это безобразие я и прячу в класс чтобы каждый раз не вспоминать (объявлена, но не инициализирована; содержит данные или нет; надо/не надо SysFreeString; как передана по значению или по ссылке и.т.д) M>3. Поэтому разрушения строки нет — есть освобождение указателя на BSTR которую всё ещё "держит" VB M>Или я не прав ?
Твой класс должен удовлетворять следующим условиям:
1. У него не должно быть данных, отличных от передаваемых. Функций — сколько угодно. Виртуальных — ни одной, чтобы не появились данные.
2. Эти данные должны быть валидными как при настоящей инициализации класса.
3. У него не должно быть деструктора, разрушающего внешние объекты, адрусуемые данными класса. Проще вообще его не иметь.
При таких условиях — ради бога.
Но проще — сделать локальную переменную, и не думать о вот таких указанных тонкостях.
Здравствуйте Vi2, Вы писали:
Vi2>Хороший стиль, не правда ли?
Х-мм. Может быть и "да". Кто-то наверняка стал бы спорить.
Но я не спрашивал "люди, помогите побороть гадкие BSTR, plz, plz, help, help"
А с чего начался разговор вы узнаете посмотрев начало темы.
[Не зря я сначала утаил ДЛЯ чего это нужно. К этому моменту, похоже, первоначальный смысл утерян навсегда]
Здравствуйте ma3ai, Вы писали:
M>Х-мм. Может быть и "да". Кто-то наверняка стал бы спорить. M>Но я не спрашивал "люди, помогите побороть гадкие BSTR, plz, plz, help, help" M>А с чего начался разговор вы узнаете посмотрев начало темы. M>[...К этому моменту, похоже, первоначальный смысл утерян навсегда]
Ну почему же утерян?!
В указанном примере конструктор CComBSTR::CComBSTR(LPCOLESTR pSrc) тоже не будет вызван просто потому, что он о нём не знает. Он же не телепат. А линковка функции пройдёт из-за extern "C" по имени _f2s@4, которое определено в другом модуле.
Я просто взял достаточно простой пример. Не более того. Он случайно совпал с BSTR.
Здравствуйте Vi2, Вы писали:
Vi2>Ну почему же утерян?!
Ну, погорячился я немного ... ничего личного
Vi2>В указанном примере конструктор CComBSTR::CComBSTR(LPCOLESTR pSrc) тоже не будет вызван просто потому, что он о нём не знает.
Надо будет проверить ... Но скорей всего так оно и есть. А жаль.
И использовать szCmdLine как CComBSTR на всю катушку — никаких проблем (с учётом тех требований к классу, кроме деструктора — он вызываться при таком раскладе не будет).
Vi2>И использовать szCmdLine как CComBSTR на всю катушку — никаких проблем (с учётом тех требований к классу, кроме деструктора — он вызываться при таком раскладе не будет).
Можно, но это всё уже делает мой класс.
Может я и изобрёл велосипед, но то что он проще — это точно. То что нужно — делает.
P.S. может это врождённое, но не люблю я эти монстрообразные порождения на все случаи жизни. Другими словами: для чего мне "шнуркозавязыватель, использующий революционные технологии в области атомного взаимодействия межмолекулярных соединений с автооткатом, системой аварийного питания и возможностью работы в полярных условиях. как опция поставляется развязыватель" ? Ну может быть пока не нужно...
Здравствуйте Кирпа В.А., Вы писали:
КВА>>>В классе 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). Остальные случаи — несопровождаемы.
На самом деле, все, как обычно, просто. Когда ты пробуешь вызывать ff из C++ непосредственно, он, видя определение функции, конструирует из строки объект cobj и передает его (или его копию) в функцию. Т.е. собственно конструирование объекта (вызов перегруженного конструктора) происходит в приложение-хостере, которое грузит DLL. После чего в стеке на входе функции появляется экземпляр объекта. А когда ты вызываешь эту функцию из VB — там ее определяешь, как ff(BSTR). Соотв, передается не экземпляр объекта (или ссылка на него), а просто указатель на строку. И в стеке на входе появляется указатель на строку. И что будет в этом случае — одному богу известно. Но что ничего хорошего — 100%. Говоря проще — объект в данном случае конструирует не DLL, а приложение-хостер. Сишное приложение это делает, т.к. видит 'правильный' тип, а VB видит BSTR — что и честно отдает.
Вообще, использование объектов при передаче параметров в таких случаях ведет ко многим проблемам, используй простые типы. Ну, как крайний случай можно рассматривать передачу объектов-интерфейсов, при условии одинаковости реализации vtable в различных языках программирования. И все равно — при этом передают указатель на интерфейс объекта, но никак не сам объект.
А мир — мир, поверь, очень многообразен.
Успехов.
M>Здравствуйте Аноним, Вы писали:
M>... экспортируемые DLL, должны принимать только обычные типы. ...
M>Похоже что так оно и есть. M>Невольно задумаешся — как всё-таки ограничен наш мир ... M>