I was trying to write a C++ program, including a header file, and a cpp file, plus, I am using the SimConnect dll.
Let's say I have a class called MyClass, which contains some functions.
Inside one of my functions, I call this SimConnect function:
SIMCONNECTAPI SimConnect_CallDispatch(HANDLE hSimConnect, DispatchProc pfcnDispatch, void * pContext);
Also, I wrote this function:
void __stdcall MyClass::myDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void *pContext)
As far as I understood, I should send this function as the second parameter to the SimConnect function SimConnect_CallDispatch I talked about before.
I read an example which came with the SimConnect SDK which is doing exactly what I am doing, while I get an error, and they don't.
The only thing that's different is that they wrote their whole code in one page, while I tried to split the code into header + body.
When I try to do as I said above, and write
SimConnect_CallDispatch(hSimConnect, myDispatchProc, NULL);
I receive this error (compiler is having trouble in myDispatchProc part):
argument of type "void (__stdcall TransmitData::*)(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext)" is incompatible with parameter of type "DispatchProc"
So clearly, it's a casting-like error.
As I said, this code would've worked if I didn't split my program into two separate files. Why doesn't it work now?
Although this is not a recent question, I'll leave a reference in case anyone else stumbles upon this. Note: This question is specific to the SimConnect SDK used for MS FS2020 (and FSX).
The SimConnect_CallDispatch function requires a reference to a static, callback function with a specific interface, i.e. (SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
The nature of the static function is that it won't have access to the variables of the instance, so to get around this problem, all of the examples out there don't bother with the complexity of .h and .cpp files and just code everything in one file. This is how I solved this issue,
Define the static callback function in the .h file, e.g.
void static CALLBACK MyDispatchProcRD(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext);
Define a separate instance function to handle the actual processing of the callback in the .h file, e.g.
void DispatchProc(SIMCONNECT_RECV* pData, DWORD cbData);
In the implementation of the static callback (.cpp) cast the context reference to your class and pass it on to the instance method for further processing, e.g.
void CALLBACK MainProcessor::MyDispatchProcRD(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext) {
MainProcessor* procThis = reinterpret_cast<MainProcessor*>(pContext);
procThis->DispatchProc(pData, cbData);
}
Just remember to pass the reference to your instance when calling SimConnect dispatch, i.e.
SimConnect_CallDispatch(hSimConnect, MyDispatchProcRD, this);
Related
I'm working on a project in C++, but at some point in the application it fails and generates a core dump. The application uses a couple of classes, which for the purposes here I'm concentrating on one of the classes, which I'm calling A, and is instantiated as object a. This has a large number of member functions, of which at the moment only a few are being used, but one of these generates a log to produce diagnostics to be used for debugging. I want to use this to find out why the application is failing.
The project is to put together code that invokes the various member functions, and although I have access to the source code and some limited documentation, none of the code can be changed, with all changes being in the code that makes use of the classes and invokes the member functions. The member function in question is:
void enable_log (log_callback callback, void * user_data = nullptr)
where the 1st argument callback contains the message and 2nd argument is optional. For now it can be set to nullptr, so would be invoked as:
a.enable_log(callback, nullptr);
From this documentation it's not at all clear what exactly callback is. However, in looking at the source code this is:
using log_callback = void (*)(const std::string& message, void* user_data);
in a header file, where log_callback is an alias for const std::string& if I understand this correctly.
I already have dummy classes on a platform using Visual Studio 2019 with some test member functions to simulate invoking the member functions on a remote Linux server, but I'm unable to find a way of making use of the member function above. I added the test member function to the dummy class as follows:
void enable_log(const std::string& callback, void* user_data = nullptr) {
callback = "ABCD";
}
which is supposed to generate a test string which is returned, such that in the real application this string will have diagnostic information that will be written to a file. However, the "=" is an error.
The idea is that in the main function an empty string will be declared, then enable_log() should populate this string, which can be printed out.
I've spent some time looking at various resources, including Stackoverflow, but I cannot find a way of returning a string with the information that can be printed out. I need a simple way to simulate this, and as I said above, I must not change the source code of the real member function, so the simulated member function has to produce a string in the same way. How is this done? Some advice would be appreciated.
Callback, in simple words, is some function that will be called later at some point. Example:
void callback_fn(int a);
using callback_t = (void)(*)(int a);
void some_func(callback_t);
You can use some_func() like so:
some_func(callback_fn);
Full example here: https://godbolt.org/z/ET3GhfYrv
For your usecase the parameters of the callback are slightly different. Here's how to read the syntax:
using log_callback = // this just creates an alias for whatever is on the right handside
void // the return type of the "callable" should be void
(*) // this tells us that it is a function pointer
(const std::string& message, void* user_data) // These are the arguments the callable takes. It is a "std::string" and a "void *"
To use this, just create a free function with the same signature:
void callable(const std::string &msg, void *userData = nullptr)
{
// msg is the data sent by the function. use it in whatever way
// you want.
std::cout << msg << '\n';
}
// Pass it to the enable_log
enable_log(callable);
I am writing some C++ code to get data from a SDR (software defined radio) and analyse it using an FFT library (FFTW3). The software uses an API (SDR_play_API and its DLL). I am trying to include the API functionality into an object. The library comes with a non-object based example program.
The IDE I am using is VS2105.
Three of the functions are callbacks to handle events from the hardware. The address of the callback functions that handle these events are passed to the API through a structure. I include the relevant code snippets from the example program, the header files and my own code after a description of the problem.
Though I have been on some C++ training - and taught others the basics - I am far from an expert and the problem goes beyond my understanding of pointers and objects.
The problem I have is one that has been discussed before on Stack Exchange. It is a problem of pointers to member functions.
The simple solution would seem to be to make the functions STATICs. However that causes problems because of some of the variables used in these functions. Without making them static then the compiler complains and if I make them static the linker complains because there is no matching definition for static variables in the library.
I have looked through Stack Exchange tried the solutions suggested but I can't seem to make any of them work - I get compiler errors.
So I feel ideally I just want to pass the pointers to the member functions to API through the structure ... but I have tried all sorts of variations on syntax without success. Can anyone help?
The API comes with an example program that does not use objects. So before I reveal my attempts to use the functions in an object, here is the code from the example program. First I will show where it assigns the location of the functions to the members of the structure, then I will include the structure definitions etc from the header file.
First then, the assignment:
// Assign callback functions to be passed to sdrplay_api_Init()
cbFns.StreamACbFn = StreamACallback;
cbFns.StreamBCbFn = StreamBCallback;
cbFns.EventCbFn = EventCallback;
Here, from the header file that comes with the API, is the definition of the structure
{
sdrplay_api_StreamCallback_t StreamACbFn;
sdrplay_api_StreamCallback_t StreamBCbFn;
sdrplay_api_EventCallback_t EventCbFn;
} sdrplay_api_CallbackFnsT;
and here, again from the header file that comes with the API is the definition of the data types - you can see the these are function pointers
typedef void (*sdrplay_api_StreamCallback_t)(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext);
typedef void (*sdrplay_api_EventCallback_t)(sdrplay_api_EventT eventId, sdrplay_api_TunerSelectT tuner, sdrplay_api_EventParamsT *params, void *cbContext);
here is one of the call back function declarations
void StreamACallback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext)
{
...
}
So that all works fine and compiles - but it that works in a non-object scenario.
However when I make the functions a member of a class then I get errors. I have tried the following variations without success. Can someone please point me in the right direction - if I had had hair by now I wouldn't have anymore cause it would be on the floor all around me in clumps.
function definition:
void sdr_object::StreamACallback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext)
{
...
}
Unsuccessful attempts to assign to structure include (including compiler error)
cbFns.StreamACbFn = this->StreamACallback;//"non standard syntax: use '&' to create a pointer to the member"
//cannot convert from 'void (__cdecl sre_object::*)(...) to sdrplay_api_StreanCallback_t
cbFns.StreamACbFn = &this->StreamACallback;//'&': illegal operation on bound member function experssion
cbFns.StreamACbFn = this->sdr_object::StreamACallback; //"non standard syntax: use '&' to create a pointer to the member"
//cannot convert from 'void (__cdecl sre_object::*)(...) to sdrplay_api_StreanCallback_t
cbFns.StreamACbFn = &this->sdr_object::StreamACallback; //'&': illegal operation on bound member function experssion
Any wisdom will e received most gratefully. (How to do it will be received even more gratefully ...!)
This answer comes with many thanks to Igor Tandetnik who suggested the solution in comments above.
Igor wrote "Make your callbacks static member functions. Pass this pointer for cbContext parameter to sdrplay_api_Init - you get that same pointer back in the callback. Now the callback can do static_cast<MyClass*>(cbContext)->memberFunction(params); . The actual logic can now be implemented in a non-static member function; the static one just serves as a glue between C++ implementation and C API"
My implementation of that is as follows:
Add to the class declaration in the header file an intermediary "glue" function for each event handler e.g.:
static void sdr_object::StreamACallback_static(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext)
{
static_cast<sdr_object*>(cbContext)->StreamACallback(xi, xq, params, numSamples, reset, cbContext);
};
For the function above, the pointer that is handed to the API then becomes
cbFns.StreamACbFn = this->StreamACallback_static;
In my particular example, I then had to do the same for the other two functions.
I did not have to change any other existing code.
Again, many thanks to Igor for the answer - I hope I have implemented it as he intended ... that said, what I have described works, so I imagine I have!
I have been struggling for days to figure out the probably obvious reason why i cant get my code to compile.
I have a class (based on wxThread) where the callback is defined:
-- Header file --
class TestClass : public wxThread
{
private:
static void WlanNotification(WLAN_NOTIFICATION_DATA *wlanNotifData, VOID *p);
};
-- Code file --
I call the WlanRegisterNotification function, that needs the above callback function as a parameter:
dwResult = WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ALL, true, (WLAN_NOTIFICATION_CALLBACK) WlanNotification, this, 0, &dwPrevNotif);
This compiles and works fine, but the problem is the function is marked as static, so i cant access my non static stuff from the callback (which i need for other reasons).
I have tried every single combination i can think of to pass in the callback as non static:
-- Header file --
void WINAPI WlanNotification(PWLAN_NOTIFICATION_DATA data, PVOID context);
-- Code file --
dwResult = WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ALL, true, (WLAN_NOTIFICATION_CALLBACK)WlanNotification, this, 0, &dwPrevNotif);
i just get:
error C2660: 'WlanRegisterNotification' : function does not take 6
arguments
error C2440: 'type cast' : cannot convert from 'overloaded-function'
to 'WLAN_NOTIFICATION_CALLBACK'
I'm thinking its related to the typedef somehow:
typedef VOID (WINAPI *WLAN_NOTIFICATION_CALLBACK) (PWLAN_NOTIFICATION_DATA, PVOID);
I have tried googling for examples of using the WlanRegisterNotification function, but none of the examples i could find is calling it from a class, which is what seems to be an issue here, so i'm really lost.
A non-static class method has a hidden this parameter that the callback is not expecting let alone know how to fill in. That is why you cannot use it as a callback unless you either 1) use static to remove that parameter, or 2) create a thunk to use as the actual callback and then have it internally delegate to a non-static class method. Remember that the Windows API is designed for C, not C++. There are no classes or implicit this pointers in C.
In this case, a static callback can access non-static members of your class because you are explicitly passing the object's this pointer as the pCallbackContext of WlanRegisterNotification(), which is then passed as-is to the context of the callback:
class TestClass : public wxThread
{
private:
static VOID WINAPI WlanNotification(PWLAN_NOTIFICATION_DATA wlanNotifData, PVOID context);
};
VOID WINAPI TestClass::WlanNotification(PWLAN_NOTIFICATION_DATA wlanNotifData, PVOID context)
{
TestClass *pThis = (TestClass*) context;
// use pThis-> to access non-static members as needed..
}
// get rid of the typecast when passing the callback. If it does
// not compile, then it is not declared in a compatible manner...
dwResult = WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ALL, TRUE, &WlanNotification, this, 0, &dwPrevNotif);
I'm having some trouble making a callback wrapper class method that needs to be used by a third party library; the JackAudio library.
I have been able to make a wrapper for a JackAudio callback function that needs two arguments.
I'm just having trouble creating a callback function for a particular function that needs a const char * as an argument.
So far I have been able to make the JackAudio library jack_set_sample_rate_callback function use a custom class and can be executed like so:
SoundClass Sound;
SoundClass * SoundPointer = &Sound;
jack_set_sample_rate_callback(
client,
SoundClass::SampleRateCallbackWrapper,
SoundPointer
);
And the class looks something like this:
SoundClass
{
int SampleRateCallback( jack_nframes_t nframes )
{
//executes some code when called.
}
static int SampleRateCallbackWrapper( jack_nframes_t nframes, void * arg )
{
return static_cast < SoundClass* > ( arg )->SampleRateCallback( nframes );
}
};
All of the above works well, with no issues.
The problem I'm having now is with the JackAudio callback function jack_set_error_function
This is what I tried:
static void ErrorCallbackWrapper( const char * arg )
{
return static_cast < SoundClass*>( arg )->SomeErrorFunction();
}
But I get error: invalid static_cast from type ‘const char*’ to type ‘SoundClass*’
I get the gist why this is happening, I just have no idea what to do for a solution.
Thanks in advance for any help guys.
Assuming the Jack API is written for the C language, there is a formal problem already with the working callback that you have. Namely that it then needs to be extern "C", and that as a static member function it cannot be. So formally it needs to be a free-standing function.
The documentation that you link to for the jack_set_error_function gives this signature, presumably expressed in C:
void jack_set_error_function( void(*)(const char *) func);
For C++ the callback must be assumed to be extern "C", so,
extern "C" void MyErrorFunction( char const* errorMessage )
{
// Whatever, e.g. post a message to the GUI event queue, or terminate.
}
If you want this function to in turn call a method on an object, then unless the library provides some special mechanism to help you, you will just have to use one of the following techniques:
a namespace scope variable accessed by the callback, or
a dynamically generated callback.
C++ does not as of yet support the second approach, at all, so the first one is strongly indicated – if you want a callback on a method of an object.
EDIT: Sorry, I forgot to mention,
the function declarations in the API documentation are syntactically invalid.
E.g. the documentation’s signature
void jack_set_info_function( void(*)(const char *) func );
simply won’t compile with a standard-conforming compiler. Not as C, and not as C++. It’s syntactically invalid in both languages.
Instead it should be
void jack_set_info_function( void(*func)(const char *) );
Since the documentation apparently is generated by DOxygen, it stands to reason that it's been generated from source code that compiles. If so then this is a bug in DOxygen, and a problem with the quality assurance of the library provider. However it might be a problem that lies solely with the library provider, or, I might be mistaken in the assumption that this is a C library?
I have this class that will help me with animated meshes.
class CAllocateHierarchy: public ID3DXAllocateHierarchy
{
public:
STDMETHOD(CreateFrame)(THIS_ LPCTSTR Name, LPD3DXFRAME *ppNewFrame);
STDMETHOD(CreateMeshContainer)(THIS_ LPCTSTR Name, LPD3DXMESHDATA pMeshData,
LPD3DXMATERIAL pMaterials, LPD3DXEFFECTINSTANCE pEffectInstances, DWORD NumMaterials,
DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo,
LPD3DXMESHCONTAINER *ppNewMeshContainer);
STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);
STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);
CAllocateHierarchy(CMyD3DApplication *pApp) :m_pApp(pApp) {}
public:
CMyD3DApplication* m_pApp;
};
but when i try to intallizes a class like CAllocateHierarchy allloc(this); i will get error. 16 IntelliSense: object of abstract class type "CAllocateHierarchy" is not allowed: c:\users\owner\documents\visual studio 2010\projects\monopoly\monopoly\monopoly.cpp 186
The error message indicates that you didn't override all the abstract methods from the interface ID3DXAllocateHierarchy.
Looking through the arguments, I see that you forgot the "const" for several arguments to the CreateMeshContainer() method. As a result, the C++ compiler thinks this is a different method, and complains that the original CreateMeshContainer() is still abstract.
From http://msdn.microsoft.com/en-us/library/bb205621(v=VS.85).aspx
HRESULT CreateMeshContainer(
[in] LPCSTR Name,
[in] const D3DXMESHDATA *pMeshData,
[in] const D3DXMATERIAL *pMaterials,
[in] const D3DXEFFECTINSTANCE *pEffectInstances,
[in] DWORD NumMaterials,
[in] const DWORD *pAdjacency,
[in] LPD3DXSKININFO pSkinInfo,
[out, retval] LPD3DXMESHCONTAINER *ppNewMeshContainer
);
So the solution is: Add 'const' to several arguments (but see update below).
Update: You also are missing some '*' in your arguments. Apparently you have declared the CreateMeshContainer to take arguments by value, while it should be by const pointer. Copy-pasting the declaration from the msdn link and removing the [in] tags might be the fastest way to get a correct declaration.
Suggestion: Using a different compiler might help to generate more insightful error messages, as might setting the warning level as high as possible. Some compilers list the methods that are still abstract. Some compilers might warn that your CreateMeshContainer() is not overriding the virtual CreateMeshContainer() in the interface. Every suggestion what is wrong could be helpful.
Your subclass doesn't implement at least one of the pure virtual methods (marked virtual whatever = 0) of its base class. You must implement all such methods in order to instantiate your subclass.