A lot of libraries these days are written in C++, but APIs such as the Win32 API for Ruby 1.8 require C-style function interfaces.
So here's a little tutorial on creating a C wrapper for a C++ library.
Of course, this is Windows only as it's Win32 DLLs we're handling.
I'm going to start with a C++ library
So we make a library with a header file as normal:
Header
Implementation
Excuse the lack of comments, this is simply a C++ class with a constructor and a deconstructor (Which is rather pointless in this class' case) with 3 methods; Set, Get and Print.
Probably the most pointless class in the world as simply having an std::string name would suffice, but it's an example.
So the first step is to create another C++ file to bind our C stuff to, this will be a C++ source file but the code in it will be written in C.
I'll call my files API.h and API.cpp
Header
Implementation
Alright, so we're ready to analyse what we need.
First thing, get your methods:
These are what we will be binding into C exports.
The way to translate these into C is to have the class object parsed as an argument in each function, however we don't want to reference the class in the C API because the class isn't accessible outside C++, so we forward-declare some prototype C structures to replace the class.
Modify the header file of API.h so it looks like this:
Header
Look at the typedef, it has no implementation defined yet, it is a forward-declared structure so only POINTERS to this structure are acceptable in this header file, this is what we need to avoid including the class's header file.
The extern "C" is C++ specific, it tells the compiler to not mangle the names of the exported functions, so in the DLL file they can be easily identified by any API looking for them. Wrap the extern "C" part in #ifdef __cplusplus if you plan to use this header file in any C programs.
You can see that the dllexport is now needed for every function we want to export and now every function has the class name, "Name", infront of the original method name, this helps anyone using the API understand that the functions all belong to the same collection of operations.
The self argument is the reference we need to the "class" instance, this is core to OOP-style C coding.
So onto the implementation, where things go a bit crazy. Remember this is C++ so, uh, anything goes?
And there we go.
If you were to check sizeof( xiName_t ) in C with sizeof( xiName ) in C++ you'll see that they are exactly the same size because xiName_t simply inherits all of xiName.
Now this library (When compiled) is fully C compatible and can be used as normal.
If C compatibility isn't a concern, you can certainly change that typedef struct xiName_s xiName_t; to a forward-declare of the C++ class: class xiName; but as classes don't exist in C, the header file loses compatibility.
So if we were to go into an engine that can use C bound DLL functions, such as RGSS, we can call this C++ class through the new C functions, just remember to string/array pack/unpack that structure in Ruby and instantiate it's bytes Ruby-side, where you'll have to modify the constructor to take said bytes as an argument:
And you won't need to deconstruct it as Ruby's garbage collect will take care of it. Of course, you may need to create a function to clean up any memory that will be leaked on the C-side, such as any pointers contained within the class, which is where things get messy as you'll have to promote these pointers to public to be able to delete them.
I hope this has inspired people to mess around with this stuff.
So here's a little tutorial on creating a C wrapper for a C++ library.
Of course, this is Windows only as it's Win32 DLLs we're handling.
I'm going to start with a C++ library
So we make a library with a header file as normal:
Header
C++:
<span style="color: #339900;">#ifndef __NAME_H__
<span style="color: #339900;">#define __NAME_H__
<span style="color: #339900;">#include <string>
class __declspec( dllexport ) xiName {
public:
xiName();
~xiName();
void Set( const char * const _name );
const char * Get() const;
void Print() const;
private:
std::string name;
};
<span style="color: #339900;">#endif
C++:
<span style="color: #339900;">#include "Name.h"
<span style="color: #339900;">#include <iostream>
using namespace std;
xiName::xiName() {
Set( <span style="color: #666666;">"" );
}
xiName::~xiName() {
Set( <span style="color: #666666;">"" ); // Don't really need to do this as name is part of the structure
}
void xiName::Set( const char * const _name ) {
name = string( _name );
}
const char * xiName::Get() const {
return name.c_str();
}
void xiName::Print() const {
<span style="color: #0000dd;">cout << name << endl; // Cursed cout streamer!
}
Excuse the lack of comments, this is simply a C++ class with a constructor and a deconstructor (Which is rather pointless in this class' case) with 3 methods; Set, Get and Print.
Probably the most pointless class in the world as simply having an std::string name would suffice, but it's an example.
So the first step is to create another C++ file to bind our C stuff to, this will be a C++ source file but the code in it will be written in C.
I'll call my files API.h and API.cpp
Header
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol><span style="color: #339933;">#ifndef __API_H
<span style="color: #339933;">#define __API_H
// TODO: write stuff
<span style="color: #339933;">#endif
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol><span style="color: #339933;">#include "API.h"
<span style="color: #339933;">#include "Name.h" // Include the C++ class header we're binding
// TODO: write stuff
Alright, so we're ready to analyse what we need.
First thing, get your methods:
- Constructor
- Destructor
- Set
- Get
These are what we will be binding into C exports.
The way to translate these into C is to have the class object parsed as an argument in each function, however we don't want to reference the class in the C API because the class isn't accessible outside C++, so we forward-declare some prototype C structures to replace the class.
Modify the header file of API.h so it looks like this:
Header
C++:
<span style="color: #339900;">#ifndef __API_H
<span style="color: #339900;">#define __API_H
typedef struct xiName_s xiName_t; // Forward declare class
// In C++ declare the following functions to be exported C-style (No mangled names)
<span style="color: #339900;">#ifdef __cplusplus
extern <span style="color: #666666;">"C" {
<span style="color: #339900;">#endif
__declspec( dllexport ) xiName_t * Name_New(); // Constructor
__declspec( dllexport ) void Name_Delete( xiName_t * const self ); // Destructor
__declspec( dllexport ) void Name_Set( xiName_t * const self, const char * const _name );
__declspec( dllexport ) const char * Name_Get( const xiName_t * const self );
__declspec( dllexport ) void Name_Print( const xiName_t * const self );
<span style="color: #339900;">#ifdef __cplusplus
}
<span style="color: #339900;">#endif
<span style="color: #339900;">#endif
The extern "C" is C++ specific, it tells the compiler to not mangle the names of the exported functions, so in the DLL file they can be easily identified by any API looking for them. Wrap the extern "C" part in #ifdef __cplusplus if you plan to use this header file in any C programs.
You can see that the dllexport is now needed for every function we want to export and now every function has the class name, "Name", infront of the original method name, this helps anyone using the API understand that the functions all belong to the same collection of operations.
The self argument is the reference we need to the "class" instance, this is core to OOP-style C coding.
So onto the implementation, where things go a bit crazy. Remember this is C++ so, uh, anything goes?
C++:
<span style="color: #339900;">#include "API.h"
<span style="color: #339900;">#include "Name.h" // Include the C++ class header we're binding
// Override the xiName typedef with an implementation
// As this is C++, the structure can inherit the class, gaining the full size of the parent class and it's public/protected methods
typedef struct xiName_s : public xiName {
} xiName_t;
xiName_t * Name_New(); {
return <span style="color: #0000dd;">new xiName_t; // As the struct inherits the base class, xiName's default constructor will be called
}
void Name_Delete( xiName_t * const self ) {
<span style="color: #0000dd;">delete( self ); // This will also call delete for xiClass
}
void Name_Set( xiName_t * const self, const char * const _name ) {
self->Set( _name );
}
const char * Name_Get( const xiName_t * const self ) {
return self->Get();
}
void Name_Print( const xiName_t * const self ) {
self->Print();
}
And there we go.
If you were to check sizeof( xiName_t ) in C with sizeof( xiName ) in C++ you'll see that they are exactly the same size because xiName_t simply inherits all of xiName.
Now this library (When compiled) is fully C compatible and can be used as normal.
If C compatibility isn't a concern, you can certainly change that typedef struct xiName_s xiName_t; to a forward-declare of the C++ class: class xiName; but as classes don't exist in C, the header file loses compatibility.
So if we were to go into an engine that can use C bound DLL functions, such as RGSS, we can call this C++ class through the new C functions, just remember to string/array pack/unpack that structure in Ruby and instantiate it's bytes Ruby-side, where you'll have to modify the constructor to take said bytes as an argument:
C++:
void Name_NewRuby( xiName_t * const bytes ); {
xiName_t * const instance = <span style="color: #0000dd;">new xiName_t;
<span style="color: #0000dd;">memcpy( ( void * )bytes, ( void * )instance, <span style="color: #0000dd;">sizeof( xiName_t ) ); // Copy the bytes from our real instance to the Ruby byte array
<span style="color: #0000dd;">delete( instance ); // Clean up the instance (Must not deconstruct any pointers contained in the class!!!)
}
C++:
void Name_DeleteRuby( xiName_t * const bytes ); {
<span style="color: #0000dd;">delete( bytes->pointerToAnotherObject ); // needs to be public
<span style="color: #0000dd;">delete( bytes->anotherPointer ); // needs to be public
// Must not call delete( bytes ) or Ruby's garbage collector will panic when it hits that memory block!
}
I hope this has inspired people to mess around with this stuff.