Struct default constructor - need some kind of solution for C++ interop
Ethan Watson via Digitalmars-d
digitalmars-d at puremagic.com
Tue Sep 6 06:44:37 PDT 2016
Alright, so now I've definitely come up across something with
Binderoo that has no easy solution.
For the sake of this example, I'm going to use the class I'm
binary-matching with a C++ class and importing functionality with
C++ function pointers to create a 100% functional match - our
Mutex class. It doesn't have to be a mutex, it just needs to be
any C++ class where a default constructor is non-trivial.
In C++, it looks much like what you'd expect:
class Mutex
{
public:
Mutex();
~Mutex();
void lock();
bool tryLock();
void unlock();
private:
CRITICAL_SECTION m_criticalSection;
};
Cool. Those functions call the exact library functions you'd
expect, the constructor does an InitializeCriticalSection and the
destructor does a DeleteCriticalSection.
Now, with Binderoo aiming to provide complete C++ matching to the
point where it doesn't matter whether a class was allocated in
C++ or D, this means I've chosen to make every C++-matching class
a value type rather than a reference type. The reasoning is
pretty simple:
class SomeOtherClass
{
private:
SomeVitalObject m_object;
Mutex m_mutex;
};
This is a pretty common pattern. Other C++ classes will embed
mutex instances inside them. A reference type for matching in
this case is right out of the question. Which then leads to a
major conundrum - default constructing this object in D.
D structs have initialisers, but you're only allowed constructors
if you pass arguments. With a Binderoo matching struct
declaration, it would basically look like this:
struct Mutex
{
@BindConstructor void __ctor();
@BindDestructor void __dtor();
@BindMethod void lock();
@BindMethod bool tryLock();
@BindMethod void unlock();
private CRITICAL_SECTION m_criticalSection;
}
After mixin expansion, it would look come out looking something
like this:
struct Mutex
{
pragma( inline ) this() { __methodTable.function0(); }
pragma( inline ) ~this() { __methodTable.function1(); }
pragma( inline ) void lock() { __methodTable.function2(); }
pragma( inline ) bool tryLock() { return
__methodTable.function3(); }
pragma( inline ) void unlock() { __methodTable.function4(); }
private CRITICAL_SECTION m_criticalSection;
}
(Imagine __methodTable is a gshared object with the relevant
function pointers imported from C++.)
Of course, it won't compile. this() is not allowed for obvious
reasons. But in this case, we need to call a corresponding
non-trivial constructor in C++ code to get the functionality
match.
Of course, given the simplicity of the class, I don't need to
import C++ code to provide exact functionality at all. But I
still need to call InitializeCriticalSection somehow whenever
it's instantiated anywhere. This pattern of non-trivial default
constructors is certainly not limited to mutexes, not in our
codebase or wider C++ practices at all.
So now I'm in a bind. This is one struct I need to construct
uniquely every time. And I also need to keep the usability up to
not require calling some other function since this is matching a
C++ class's functionality, including its ability to instantiate
anywhere.
Suggestions?
More information about the Digitalmars-d
mailing list