I'm very new to using std and I'm currently trying to call into a function that takes an std::function as a param. Something similar to below:
In the .h file in one lib:
typedef bool t (/*Params*/);
void __stdcall Foo(std::function<t> &function) {m_function = function;}
std::function<t> m_function;
I imported the lib and tried to use Foo in another cpp file:
bool Implementation (/*Params*/)
{
// Implementation
}
void Bar()
{
Foo(std::function<t> (Implementation));
}
I'm getting a linker error (LNK2019) when I compile for x86 (but not x64) because of the calling convention:
Unresolved External Symbol __stdcall Foo (class std::tr1::function<bool __cdecl(/*Params*/) const&)
From this I gathered that I need to mark "t" and Implementation as __stdcall but doing so causes other compile failures. I should also note the code compiled correctly when it was being built in the same lib. Is there a way to associate a calling convention to an std::function?
Try:
void Foo(const std::function<bool()> &func)
{
func();
}
bool Implementation (/*Params*/)
{
cout << "Implementation"<<endl;
return true;
}
void Bar()
{
Foo(std::function<bool()>(&Implementation));
}
int main()
{
Bar();
return 0;
}
Related
For simplicity, I will omit things like proper typedefs to opaque structs instead of void *, Windows calling conventions, fixed integer types, etc.
Suppose I have the following files:
CApi.h -- Shared library header with C linkage for portability and hiding proprietary code.
#define LIB_API // library import/export details
extern "C" {
typedef int error;
error LIB_API lib_foo_create(void ** foo);
error LIB_API lib_foo_destroy(void * foo);
error LIB_API lib_foo_func(void * foo, int * out);
error LIB_API lib_bar_create(void ** bar);
error LIB_API lib_bar_destroy(void * bar);
error LIB_API lib_bar_func(void * bar, int * out); // Suppose this internally uses Foo::func from CxxApi.hpp with C++17
} // extern C
CxxApi.hpp -- Header only wrapper to simplify API usage.
#include "CApi.h"
namespace lib {
namespace detail {
template < typename Return = void, typename Func, typename... Args >
Return c_api(Func func)
{
// Not sure how this affects ODR if client and provider code use different versions,
// but neither see each other's code usage.
#if __cplusplus >= 201703L // C++17
// more efficient implmentation (e.g. fold expressions)
#else
// fallback implmentation (e.g. recursion)
#endif
}
} // namespace lib::detail
class Foo
{
public:
Foo() { detail::c_api(lib_foo_create, &handle_); }
~Foo() { detail::c_api(lib_foo_destroy, handle_); }
int func() { return detail::c_api<int>(lib_foo_func, handle_); }
private:
void * handle_;
};
struct Bar
{
public:
Bar() { detail::c_api(lib_bar_create, &handle_); }
~Bar() { detail::c_api(lib_bar_destroy, handle_); }
int func() { return detail::c_api<int>(lib_bar_func, handle_); }
private:
void * handle_;
};
} // namespace lib
If I, the API provider, compile this using C++17, but the client using the API uses C++11, is ODR violated with lib::detail::c_api?
I believe it is not because lib_bar_func's definition is in a different translation unit than client code, but I am not positive.
I am working with Unity and I need to pass callback delegate from C# unity script to .dll
There is a api (MyApi.h) that I have on .dll side
typedef void(__stdcall * FuncPtr) (const char * str);
static FuncPtr DebugLog = nullptr;
static void debug_in_unity(std::string message)
{
if (DebugLog)
{
DebugLog(message.c_str());
}
}
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback)
{
if (callback)
{
DebugLog = callback;
}
}
...
}
so, when I call register_debug_callback function from C# side I see that everything is ok and DebugLog assigned as expected.
Then in order to send my log message from .dll side to C# I need to call this function debug_in_unity()
So, I have another myfile.cpp file where I need to use this log function
#include "MyApi.h"
void MyClass::foo()
{
std::string log = "HERE!!!";
debug_in_unity(log);
}
So, everything looks fine, I have a global static method debug_in_unity and global func DebugLog that I assigned previously here register_debug_callback
But what actually is happen is - when I call this method register_debug_callback I see that I assigned DebugLog variable, but then when I call this method debug_in_unity I see that DebugLog is null. Looks like static variable is kind of not global like MyApi.h has instance of DebugLog and myfile.cpp has his own instance. I assume that it is a reason why I see assignment and then I see that the same value is null...
But how to use it properly? How to fix it?
You are breaking the one definition rule. You will need to have one translation unit (.c or .cpp file) defining DebugLog, and the header declaring it extern. Similarly your functions are also defined in multiple translation units.
MyApi.h
typedef void(__stdcall * FuncPtr) (const char * str);
extern FuncPtr DebugLog;
void debug_in_unity(std::string message);
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback);
// ...
}
MyApi.cpp
#include "MyApi.h"
FuncPtr DebugLog = nullptr;
void debug_in_unity(std::string message)
{
if (DebugLog)
{
DebugLog(message.c_str());
}
}
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback)
{
if (callback)
{
DebugLog = callback;
}
}
// ...
}
I had two projects which are written by C/C++.
Project 1 output is exe file and named MyProject.
Project 2 output is dll file and named Bridge.
I try to let Bridge to execute the function in MyProject. The function seems work, but I encounter an error
"Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention."
I doubt that the root cause may happen in __cdecl __stdcall convention, but still don't know how to solve it. All the project's Calling convention setting are __cdecl(/Gd), and VS IDE is VS 2013.
DLL Project (Bridge) Code
Header
typedef void(*sfcTrace)(const char *logLevel, const char *logMessage);
class OnRequestHandler
{
public:
virtual void writeLog(sfcTrace callback) = 0;
};
class GenericInfoHandler : public OnRequestHandler
{
public:
GenericInfoHandler();
~GenericInfoHandler() { delete this; };
void writeLog(sfcTrace callback);
};
extern "C" __declspec (dllexport) OnRequestHandler* __cdecl oneBridgeCallBack()
{
return new GenericInfoHandler;
}
CPP
void GenericInfoHandler::writeLog(sfcTrace callback)
{
const char *level = "DEBUG";
const char *message = "TEST";
callback(level, message);
}
MyProject Source code:
typedef OnRequestHandler* (__cdecl *test)();
HINSTANCE getDLL = LoadLibrary("Bridge.dll");
if (!getDLL)
{
cout << "Cannot not load DLL." << endl;
}
test func = (test)::GetProcAddress(getDLL, "oneBridgeCallBack");
if (!func)
{
cout << "Cannot not locate the function." << endl;
}
OnRequestHandler* instance = func();
instance->writeLog(&MyProject::TestCallBack); <----- Error Occurs here
Function implementation in MyProject
void MyProject::TestCallBack(const char *level, const char *message)
{
if (strcmp(level, "INFO") == 0){
// do something
}
else if (strcmp(level, "DEBUG") == 0){
// do something
}
}
Header:
typedef void(MyProject::*TestCallBack)(const char *logLevel, const char *logMessage);
class OnRequestHandler
{
public:
virtual void writeLog(sfcTrace callback) = 0;
};
class GenericInfoHandler : public OnRequestHandler
{
public:
GenericInfoHandler();
~GenericInfoHandler() { delete this; };
void writeLog(sfcTrace callback);
};
Try the following: (I am presuming that MyProject::TestCallback is not a static method, and it is it's use as a plain function that causes the stack corruption)
Update
typedef void(*sfcTrace)(const char *logLevel, const char *logMessage); to take an additional void * argument that will be supplied alongside the callback. I.e.:
typedef void(*sfcTrace)(void *cb_arg, const char *logLevel, const char *logMessage);
^^^^^^^^^^^^
Then update OnRequestHandler::writeLog(sfcTrace callback) and its overrides to take the additional argument from the caller. I.e.,
virtual void OnRequestHandler::writeLog(sfcTrace callback, void *cb_arg) = 0;
^^^^^^^^^^^^
void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg);
^^^^^^^^^^^^
Update the implementation of writeLog to pass this new arg to the callback:
void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg)
{ ^^^^^^^^^^^^
const char *level = "DEBUG";
const char *message = "TEST";
callback(cb_arg, level, message);
} ^^^^^^
Now we can write a new non-member callback function that is capable of calling a MyProject object's methods, so long as a MyProject object is passed via cb_arg:
void myprj_method_callback(void *arg, const char *logLevel, const char *logMessage) {
MyProject *mp = (MyProject *)arg;
mp->TestCallBack(level, message);
}
Finally, we can update the call to pass the new call-back function, and argument:
instance->writeLog(myprj_method_callback, (void *)this);
As an aside, it is generally good practice on all callbacks to let the callback supplier pass an argument that will in turn be passed to the callback when it is called. This lets the user of a callback mechanism convey the relevant data-structures to the callback function without having to store them in global variables.
I have following class inside dll
header
class __declspec(dllexport) MyClass
{
public:
bool fun1(const CString& csFilename, int& nFileID );
bool fun2(int nFileID, int nImageNum, int nStartIndex);
};
cpp
bool MyClass::fun1(const CString& csFilename, int& nFileID )
{
}
bool Myclass::fun2(int nFileID, int nImageNum, int nStartIndex)
{
}
main
void main()
{
MyClass *p = new MyClass;
p->fun1(...); //if I comment this code compiles and builds
p->fun2(...); //this is ok
}
I am getting error LNK2019: unresolved external symbol "public: bool __thiscall...
only for fun1 not for fun2 even both are in same class why ?
thanks in advance.
I am not sure what is wrong here as it works after I replace CString with std string but I am not able to reproduce same error in another small example.
# admin I think this thread should be deleted as i am not able to reproduce it.
#include <QFile>
#include <QString>
// this is some sort of low-level C function
void lowLevelOpenFD(int fd)
{
qDebug("Opened by fd: %d", fd);
}
// as well as this one
void lowLevelOpenName(const char *name)
{
qDebug("Opened by name: %s", name);
}
// this is a wrapper around low-level functions
template<typename FileId>
void callLowLevelOpen(FileId id);
template<>
void callLowLevelOpen(const QString &fileName)
{
lowLevelOpenName(QFile::encodeName(fileName).constData());
}
template<>
void callLowLevelOpen(int fd)
{
lowLevelOpenFD(fd);
}
// this is the function where the most stuff happens
template<typename FileId>
void openInternal(FileId id)
{
// lots of useful stuff goes here
// now we call one of the two low level functions
callLowLevelOpen(id);
// more useful stuff
}
// this is high-level interface to the "open by name" function
void openFile()
{
QString name = "file";
openInternal(name);
}
// this is high-level interface to the "open by FD" function
void openFile(int fd)
{
openInternal(fd);
}
int main()
{
openFile();
openFile(17);
return 0;
}
The problem is that the example above results in
error LNK2019: unresolved external symbol "void __cdecl callLowLevelOpen<class QString>(class QString)" (??$callLowLevelOpen#VQString####YAXVQString###Z) referenced in function "void __cdecl openInternal<class QString>(class QString)" (??$openInternal#VQString####YAXVQString###Z)
As far as I can see it happens because the compiler instantiates openInternal<QString>() when it's called from the first high-level overload. OK, so I thought and modified the code:
// this is high-level interface to the "open by name" function
void openFile()
{
QString name = "file";
openInternal<const QString&>(name);
}
The same problem. And I thought I told the compiler to instantiate the openInternal<const QString&>, so why it is still complains about <class QString>? I also tried this one:
// this is high-level interface to the "open by name" function
void openFile()
{
QString name = "file";
openInternal<const QString&>(static_cast<const QString&>(name));
}
Now this just looks silly and it still doesn't work. I can't explicitly specialize the openInternal() one because it's too large and the very point of this templated mess is to avoid unnecessary code duplication. I can't just rename the low level functions to turn them into overloaded ones, because they are in a third-party C library. The only thing I can do is to replace the first callLowLevelOpen() specialization with
template<>
void callLowLevelOpen(QString fileName)
{
lowLevelOpenName(QFile::encodeName(fileName).constData());
}
Then it works. There is also virtually zero performance penalty so this is a perfectly valid workaround, but I just want to understand what is going on here.
The code above was just an SSCCE, the real code is there if anyone interested. This particular issue is with the gzopen()/gzdopen(), QuaGzipFilePrivate::open() and QuaGzipFile::open() functions.
Since you actually change the signature, I think you actually don't want to specialize your function template but rather use overloading. Here is complete test program using std::string (I somehow prefer depending only on standard classes if the problem can be reproduced there as well):
#include <string>
template <typename T> void f(T);
// #define BROKEN
#if defined(BROKEN)
template <> void f(std::string const&) {}
#else
void f(std::string const&) {}
#endif
int main()
{
std::string s;
f(s);
}
If you #defined BROKEN in this code it won't work.
The reason for this behavior is that the compiler choose the overload based on the primary template. This will never add the const& part. Once this is done the compiler looks for potential specializations of the chosen overload. Since this will never have deduced the notation used for specialization this isn't picked up.
Why then is the f<std::string const&>(s) not picking up the specialization? For me it is, trying with both gcc and clang.