In a win32 application, what are the assembly code patterns of a function call?
For example I have this class member function:
void Foo::bar(MyClass* p, int i, int j, CArray<CArray<int,int>,int> &a){
// Function body
}
Related
I am trying to make an HTTP class, and I want to use C++11 (not C++14 yet) callbacks via lambdas. I have 2 mockups available, the first one works... but looks ugly. The second one I am aiming, is not compiling (error at the end).
I cannot use std::function, as this is an embedded project, and that template generates a lot of code.
#include <cstring>
class HTTP
{
public:
void get1(const char* url, void* context, void (*callback)(void*, const char*) )
{
callback(context, "");
}
void get2(const char* url, void (*callback)(const char*) )
{
callback("");
}
};
void test()
{
int k;
HTTP http;
http.get1( "http://google.com", &k, [](void* context, const char* s){
int *k = (int*) context;
*k = strlen(s);
});
// this does not compile, looking for other alternatives
http.get2( "http://google.com", [&k](const char* s){
k = strlen(s);
});
}
Error from gcc (xtensa-esp32-elf-g++ (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) 5.2.0)
HttpRequests.cpp: In function 'void test()':
HttpRequests.cpp:29:6: error: no matching function for call to 'HTTP::get2(const char [18], test()::<lambda(const char*)>)'
});
^
HttpRequests.cpp:11:10: note: candidate: void HTTP::get2(const char*, void (*)(const char*))
void get2(const char* url, void (*callback)(const char*) )
^
HttpRequests.cpp:11:10: note: no known conversion for argument 2 from 'test()::<lambda(const char*)>' to 'void (*)(const char*)'
Lambdas without a capture list are compatible with function pointers, so your first lambda can be passed as an argument to get1(). However, lambdas with a capture list are not convertible to function pointers so it can not be passed to get2().
Lambdas with captures have state but functions can not have state, which is why such lambdas are not convertible to function pointers.
The most common way to have a function accept any lambda (or any callable object) is to use function templates:
class HTTP {
// ...
template <typename Callable>
void get1(const char* url, void* context, Callable callback)
{
callback(context, "");
}
template <typename Callable>
void get2(const char* url, Callable callback)
{
callback("");
}
}
Being function templates, code size might become an issue. If that's not acceptable, then keep your current functions and restrict yourself to never passing lambdas that use captures.
In certain cases when programming with libraries written in C involving callbacks, I like to use Lambda expressions; however, if I need to alter the state of a class member variable I can't juts pass this into a stateless(function pointer) lambda. But I can assign this to a data in a context structure. What I find strange is being able to access that member variable even if it's private in the class.
Here's an example code I wrote to demonstrate.
#include <iostream>
using std::cout;
typedef struct extradatatype{
void* data;
}extradata;
extradata e = {0};
typedef void(*callback)(extradata* e);
void cb(callback c){
c(&e);
}
class Test{
private:
int x;
public:
Test(int x){
this->x = x;
}
void setcb(){
cb([](extradata* e){
Test* self = reinterpret_cast<Test*>(e->data);
self->x = 20;
});
}
int getx(){
return x;
}
};
int main(){
Test t(10);
e.data = &t;
t.setcb();
cout << t.getx();
return 0;
}
In the Lambda expression Test* self is assigned to e->data but I can access self->x as if it were a public member instead of private. So what I'm confused about is, is the lambda expression expression being executed within the stack/context of the setcb function or is it being executed elsewhere as its own function but C++ is doing some weird trick to allow private members to be accessed. Because I assume a stateless lambda is really no different than a non member static function which has no access to private members of a class.
Since your lambda function is defined within the class Test context, it will have access to class Test private member (regardless if it's this.x or self.x where self is of type Test). It is similar to this example:
class Example {
private:
int x;
public:
int f(Example e) {
return e.x;
}
};
where, since f is a member of Example, it can access e.x because e has type Example.
If you move your lambda function definition out of the class context you'll see the expected error message:
void outside(extradata* e);
class Test{
private:
int x;
public:
void setcb(){
cb(outside);
}
};
void outside(extradata* e) {
Test* self = reinterpret_cast<Test*>(e->data);
self->x = 20; // error here!
}
test.cpp:32:11: error: 'int Test::x' is private within this context
self->x = 20;
^
I have a std map that combines a string with a function pointer like:
std::map<std::string, void (*)()> funcs {
{"print", &h::print},
{"scan", &h::scan_cmd},
{"connect", &h::stream},
{"stream", &h::stream}
};
where h is the enclosing class in which this map has been initialized:
class h {
public:
void print();
void scan();
void connect();
void stream();
std::map<std::string, void (*)()> funcs {
{"print", &h::print},
{"scan", &h::scan_cmd},
{"connect", &h::stream},
{"stream", &h::stream}
};
};
I get this error:
No matching constructor for initialization of 'std::map<std::string, void (*)()>' (aka 'map<basic_string<char>, void (*)()>')
I've also tried puttting the map in this form:
std::map<std::string, void (*)()> funcs;
funcs["print"] = &print;
funcs["scan"] = &scan_cmd;
funcs["connect"] = &stream;
funcs["stream"] = &stream;
But then I got this error:
Size of array has non-integer type ' const char [6]'
I'm not exactly sure where the problem is - my guess is that it's with the void (*) () portion. I'm sure this is a c++ 11 compiler.
void (*)()
is pointer to ordinary function which takes no arguments and returns no value.
In your example print, stream, scan_cmd are non-static member functions of h class. Syntax to define pointer to member functions of h class looks like
void (h::*)()
Try:
std::map<std::string, void (h::*)()> funcs {
{"print", &h::print},
{"connect", &h::stream},
{"stream", &h::stream}
};
//In A.h I have the following code
include "afxwin.h"
include "msclr\auto_gcroot.h"
using namespace System;
using msclr::auto_gcroot;
namespace A
{
public ref class A
{
public:
virtual bool Func();
A();
~A();
virtual bool Connect();
protected:
DWORD WINAPI threadConnect(void* pParam);
};
public class AHelper
{
public:
auto_gcroot A;
};
}
//In A.cpp I have below code
// This is the main DLL file.
include "stdafx.h"
include "A.h"
include "string"
include "sstream"
include "stdlib.h"
include "strsafe.h"
include "windows.h"
include "tchar.h"
namespace A
{
A::A()
{
m_FuncHandle = mpsNil;
}
A::~A()
{
}
bool A::Func()
{
return true;
}
bool A::Connect()
{
AHelper* AHelper;
m_retVal = false;
AHelper = new AHelper();
AHelper->A = this;
HANDLE Handle_Of_Thread = 0;
DWORD dwThreadId;
//DWORD WINAPI threadConnect(void* pParam);
//If I declare the function declaration here I am getting
//error LNK2001: unresolved external symbol "unsigned long __stdcall threadConnect(void *)"
(?threadConnect##YGKPAX#Z)
Handle_Of_Thread = CreateThread
(NULL, 0, threadConnect, AHelper, 0, &dwThreadId); // with this code I am getting
//error C3867: 'A::A::threadConnect': function call missing argument list; use '&A::A::threadConnect' to create a pointer to member
return m_retVal;
}
DWORD WINAPI A::threadConnect(void* pParam)
{
AHelper* AHelper = reinterpret_cast(pParam);
//Here I need to call Func
return 0;
}
}
the usual trick is to have a static function that takes an id of some sort, this function determines which someFunc() to call (as each object will have its own someFunc, you obviously need to know which one) either using a lookup, or as is common in C/C++ apps, by passing the address of the object directly.
so you have something like:
static bool thread_func(object o)
{
return o->someFunc();
}
The trick is that the static function must be reentrant, so holds no state itself for the threads to interfere with (or if it does, make sure its thread safe)
That all supposes you're calling a method on an object that was not created within the thread. If you're just calling a function and you already have the object created within your thread, just call the function!
I would like to solve this issue about class member function callback.
Imagine you have a function from an external library (which cannot be modified!) like this:
void fortranFunction(int n, void udf(double*) );
I would like to pass as the udf function above a function member of an existing class. Please look at the following code:
// External function (tipically from a fortran library)
void fortranFunction(int n, void udf(double*) )
{
// do something
}
// User Defined Function (UDF)
void myUDF(double* a)
{
// do something
}
// Class containing the User Defined Function (UDF)
class myClass
{
public:
void classUDF(double* a)
{
// do something...
};
};
int main()
{
int n=1;
// The UDF to be supplied is myUDF
fortranFunction(n, myUDF);
// The UDF is the classUDF member function of a myClass object
myClass myClassObj;
fortranFunction(n, myClassObj.classUDF); // ERROR!!
}
The last line of the code above results in a compilation error, because you cannot declare the classUDF member function as a static function.
Do you know if it is possible to solve this issue?
Probably Boost libraries could help me, but I do not know how (please consider that fortranFunction cannot be modified because is from an external library).
Thanks a lot!
Alberto
I don't understand, why can't you declare classUDF as static like this
class myClass {
public:
static void classUDF(double *a) {
...
}
};
and then pass it like
fortranFunction(n, myClass::classUDF);
You might try that solution (a little bit hacky, but I think, it should work for you):
void fortranFunction(int n, void udf(double*))
{
double d = static_cast<double>(n);
udf(&d);
}
class myClass {
public:
void classUDF(double* a) {
}
};
#ifdef _MSC_VER
#define THREADLOCALSTATIC __declspec(thread) static
#define THREADLOCAL
#else
#define THREADLOCALSTATIC static ___thread
#define THREADLOCAL ___thread
#endif
struct _trampolinebase {
THREADLOCALSTATIC _trampolinebase* current_trampoline;
};
THREADLOCAL _trampolinebase* _trampolinebase::current_trampoline = 0;
#undef THREADLOCAL
#undef THREADLOCALSTATIC
template<class CBRET, class CBARG1, class T>
struct _trampoline1 : _trampolinebase
{
typedef CBRET (T::*CALLBACKFN)(CBARG1);
_trampoline1(T& target, CALLBACKFN& callback)
: callback_(callback)
, target_(target)
{
assert(current_trampoline == 0);
current_trampoline = this;
}
static CBRET callback(CBARG1 a1) {
_trampoline1* this_ = static_cast<_trampoline1*>(current_trampoline);
current_trampoline = 0;
return this_->trampoline(a1);
}
private:
CBRET trampoline(CBARG1 a1) {
return (target_.*callback_)(a1);
}
CALLBACKFN& callback_;
T& target_;
};
template<class FRET, class FARG1, class CBRET, class CBARG1, class T, class F>
FRET call1_1(T& target, CBRET (T::*callback)(CBARG1), F& fortranfunction, FARG1 a)
{
typedef typename _trampoline1<CBRET, CBARG1, T> trampoline;
trampoline t(target, callback);
return fortranFunction(a, trampoline::callback);
}
int main()
{
int n=1;
myClass myClassObj;
call1_1<void,int,void,double*>(myClassObj, &myClass::classUDF, fortranFunction, 1);
}
With the 'threadlocal' stuff, this will work in multithreaded calls, too. You may omit that, if you don't use a multithreaded environment. It also works with recursive calls (e.g. if the callback calls another fortran function).
This solution works only for one single argument plus callback for the fortran function and one single argument in the callback function itself, but you should be able to extend it easily. This is also, why I called it 'call1_1' (fortran function with 1 argument, callbackfunction with 1 argument). FRET is the return type of the fortran function, FARG1 the type of the first argument (int in this case). CBRET and CBARG are the same for the callback function.
Before the fortran function is actually called, the target object is stored within a global (thread-local) variable. The fortran function calls a static callback function, which finally calls your member function.
I invented the trampolinebase to instantiate the static member, I could also have used a global variable for that (but for some reason, I don't like global variables too much) ;-)