Passing C++ class to DLL for callbacks from D (Steam)

cc cc at nevernet.com
Fri Jun 8 00:55:35 UTC 2018


Hello, I'm attempting to interface with the Steam API DLL in D 
and running into some trouble working with callbacks.  I'm aware 
there's already a project here http://derelict-steamworks.dub.pm/ 
but it seems to have not yet addressed the same issue.  Steam 
provides ways to poll for whether an asynchronous request has 
completed yet and retrieve the results, but what I am trying to 
implement is receiving actual callbacks from the API.

Just for reference, I've gotten simple callbacks working in other 
APIs (passing a D function pointer to a C function and having it 
be called back), and the other basic Steam functions that don't 
deal with callbacks all work fine (defined in D as extern(C)).  
Here's where I'm running into trouble:

Definitions in C++ headers provided by Steam:

typedef uint64 SteamAPICall_t;
S_API void S_CALLTYPE SteamAPI_RegisterCallResult( class 
CCallbackBase *pCallback, SteamAPICall_t hAPICall );
S_API void S_CALLTYPE SteamAPI_UnregisterCallResult( class 
CCallbackBase *pCallback, SteamAPICall_t hAPICall );

class CCallbackBase
{
public:
	CCallbackBase() { m_nCallbackFlags = 0; m_iCallback = 0; }
	// don't add a virtual destructor because we export this binary 
interface across dll's
	virtual void Run( void *pvParam ) = 0;
	virtual void Run( void *pvParam, bool bIOFailure, SteamAPICall_t 
hSteamAPICall ) = 0;
	int GetICallback() { return m_iCallback; }
	virtual int GetCallbackSizeBytes() = 0;

protected:
	enum { k_ECallbackFlagsRegistered = 0x01, 
k_ECallbackFlagsGameServer = 0x02 };
	uint8 m_nCallbackFlags;
	int m_iCallback;
	friend class CCallbackMgr;

private:
	CCallbackBase( const CCallbackBase& );
	CCallbackBase& operator=( const CCallbackBase& );
};


It's actually more complex than that as it has templated classes 
that inherit from that base so that there is a different template 
for each expected struct that gets returned from various async 
requests.  I'm trying to get the simplest possible scenario 
working first and hoping I'm not missing anything, but I don't 
actually understand how I'm supposed to work with the C++ objects 
enough really to be sure.  Here's how I've defined it in D, as 
per https://dlang.org/spec/cpp_interface.html#classes :

alias ulong SteamAPICall_t;
extern(C) {
	void SteamAPI_RegisterCallResult(CCallbackBase pCallback, 
SteamAPICall_t hAPICall);
	void SteamAPI_UnregisterCallResult(CCallbackBase pCallback, 
SteamAPICall_t hAPICall);
}

extern(C++) {
	interface CCallbackBase {
		//this() { m_nCallbackFlags = 0; m_iCallback = 0; }
		void Run( void *pvParam );
		void Run( void *pvParam, bool bIOFailure, SteamAPICall_t 
hSteamAPICall );
		int GetICallback();
		int GetCallbackSizeBytes();

		enum { k_ECallbackFlagsRegistered = 0x01, 
k_ECallbackFlagsGameServer = 0x02 }
		//uint8 m_nCallbackFlags;
		//int m_iCallback;
		//friend class CCallbackMgr;

		//CCallbackBase( const CCallbackBase& );
		//CCallbackBase& operator=( const CCallbackBase& );
	}
}

class CImpl : CCallbackBase {
	extern(C++) {
		this() { m_nCallbackFlags = 0; m_iCallback = 0; }
		void Run( void *pvParam ) { writeln("Run1"); }
		void Run( void *pvParam, bool bIOFailure, SteamAPICall_t 
hSteamAPICall ) { writeln("Run2"); }
		int GetICallback() { return m_iCallback; }
		int GetCallbackSizeBytes() { return 
NumberOfCurrentPlayers_t.sizeof; } // ordinarily use templates to 
determine what type struct ptr to return
	}
	uint8 m_nCallbackFlags;
	int m_iCallback;
}


Which then gets called later on as:

auto cbk = new CImpl();
cbk.m_iCallback = NumberOfCurrentPlayers_t.k_iCallback;
auto hid = 
SteamAPI_ISteamUserStats_GetNumberOfCurrentPlayers(...);
SteamAPI_RegisterCallResult(cbk, hid);

And then every frame SteamAPI_RunCallbacks(); runs, which is 
supposed to fire off the callbacks.  I've also tried using 
abstract class instead of interface.  My expectation of what's 
supposed to happen is that one of those Run() functions gets 
called at some point.  However, it never does (even though I can 
poll to determine the call has actually completed).  I'm hoping 
the problem is I'm simply not passing the right expected type of 
data to the registration function and/or haven't defined the 
interface in D properly (I admit I have very little idea how the 
C++ is working anymore, including what those private methods are 
for), and not an issue with utilizing the API itself, since I 
doubt there's anyone I could ask on the Steam end of things about 
interfacing with D.

My gut feeling is I'm doing something very incorrect/stupid on 
the definition end of things, I understand passing function 
pointers to C++ functions that intend to call back well enough 
but I'm confused about this concept of passing "class SomeClass 
*" to a function and expecting it to call a method on that class, 
I don't know where everything is supposed to "exist".  If anyone 
has any insight to provide it would be greatly appreciated, 
thanks!


More information about the Digitalmars-d-learn mailing list