У меня есть большой класс из внешней библиотеки, и мне нужно использовать неуправляемый обратный вызов в управляемом коде. Неуправляемый код упрощен:
typedef std::function<void(const std::string &, Float)> ProgressCallback;
class MeshGenerator{
public:
ProgressCallback progress;
/// <summary>
/// set callback
/// </summary>
/// <param name = "_callback"></param>
inline void SetCallback(ProgressCallback _callback){ this-> progress =_callback; }
};
Теперь мой код управления упрощен:
public delegate void CallbackDelegate(String^ cap, float data);
public ref class MeshWrapper
{
private:
MeshGenerator *gen;
public:
[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
CallbackDelegate^ _Delegate;
inline MeshWrapper(){
this->gen = new MeshGenerator();
_Delegate = gcnew CallbackDelegate(this, &MeshWrapper::Progress);
auto ptr = Marshal::GetFunctionPointerForDelegate(_Delegate).ToPointer();
ProgressCallback *p = static_cast<ProgressCallback*>(ptr);
this->gen->SetCallback(*p);
}
inline void Progress(String^ cap, float s){ System::Console::WriteLine(cap + s);}
};
этот код компилируется, но в конструкторе MeshWrapper статическое приведение ("static_cast<ProgressCallback*>(ptr);") дает мне пустой делегат.
Вероятно, я делаю некоторые концептуальные ошибки. Спасибо за вашу помощь.
@Selvin, спасибо за ответ, но я не могу изменить неуправляемый класс... мне нужно найти способ использовать ProgressCallback в управляемом коде..
Прошло некоторое время с тех пор, как я писал код на C++, но у вас должна быть возможность создать неуправляемый вспомогательный класс, у которого есть gcroot для вашего meshWrapper. Таким образом, вам не придется полагаться на какие-либо автоматические преобразования.
Вы не можете просто привести указатель функции к указателю std::function
, вам нужно создать объект std::function
:
auto pc = ProgressCallback(reinterpret_cast<void(*)(const std::string &, Float)>(ptr));
this->gen->SetCallback(pc);
В качестве альтернативы можно передать лямбду, которая выполняет прямое приведение и, возможно, исправляет некоторые аргументы:
this->gen->SetCallback([=](const std::string & s, Float f) {
auto fn = reinterpret_cast<void(*)(const std::string &, float)>(ptr);
fn(msclr::interop::marshal_as<String^>(s), f);
});
спасибо, я близок к решению проблемы, первый метод работает, но когда мой метод Managed Progress вызывается, строка ^ cap имеет неправильное значение, вероятно, потому, что мне нужно преобразование из std::string в String^... второй не компилируется с этим сообщением: «Локальная лямбда C++ не разрешена в функции-члене управляемого класса»...
Спасибо @Botje за помощь, теперь я решил свою проблему следующим образом:
public delegate void CallbackDelegate(std::string& cap, float data);
Я изменил своего делегата обратного вызова в управляемом коде с помощью std::string&.
reinterpret_cast такой же, как и @Botje.
В управляемом метод Progress меняется следующим образом:
void MeshWrapper::Progress(std::string& cap, float s) {
// Transform std string in String^!!!!!!
String^ newSystemString = gcnew String(cap.c_str());
// Code that uses newSystemString here :)
};
Спасибо.
обратный вызов на встроенный? ... также
std::string
не совместим со строкой dotnet