There was a convo a while back about using the import expression and
CTFE/templates to automatically convert C/C++ .h files to .d files
without requiring an external program like bcd.gen or htod... Well, I
had a need for just such a thing, so I wrote a quick one up (that will
only work if you've written the header file in a special format). I
attached the relevant code, if you understand what's going on, you could
probably modify it. This isn't a "release" of anything, since it has no
comments, etc., but just an interesting proof-of-concept.
This D code:
mixin(sharedFromHeader_mixin!("platif.h")(LIB_NAME));
Generates the contents of the attached "formatted.d" (except not
formatted, obviously :-)).
Basically, you need this macro in your C++ header:
#define FUN(__NAME__, __RV__, __PARAMS__) __RV__ __NAME__ __PARAMS__
You'll also want to redefine some types like so:
#define _CONST const
#define _VOID void
#define _LINT long // int masquerading as a long
#define _LUINT unsigned long
#define _INT int
#define _UINT unsigned int
#define _LONG __int64
#define _ULONG unsigned __int64
#define _CHAR char
To add new types to the D conversion function, see the convertCppTypes
in sharedlib.d
Then anything you want copied directly into the D file, you need to wrap
in //@COPY and //@END:
//@COPY
struct MofIf
{
_INT (*Stream_New)(_VOID**);
_INT (*Stream_Read)(_VOID*, _VOID*, _LUINT, _LUINT*);
_INT (*Stream_Seek)(_VOID*, _ULONG);
_INT (*Stream_Tell)(_VOID*, _ULONG*);
_INT (*Stream_GetSize)(_VOID*, _ULONG*);
_INT (*Stream_GetAvailable)(_VOID*, _ULONG*);
};
//@END
Then write your functions in an //@FUNCTION //@END block:
//@FUNCTIONS
FUN(initMof, _VOID, (MofIf* _mof));
FUN(mimeOnFire, _INT, (_CONST _CHAR* path));
//@END
Anything in the header outside those blocks will be ignored. Also the
syntax is extremely specific, so the line must start with "FUN(" and end
with ");" without spaces (you can have comments after that).
Be warned, though -- compile times are SLOOW, even with only two
functions :-). I just started this part of the project, so when there's
the 70+ I expect to have when it's done... uh oh. A better replace
function could solve this (anyone have a good CTFE replace function
sitting around? :-P)
There's also a simpler function used to wrap dynamic libraries. It's
used like:
mixin(shared_mixin(LIB_NAME, [
"int function(char*)", "mimeOnFire",
"void function(MofIf*)", "initMof"]));
which will generate function pointers for all the functions and
"_loadLib()" and "_unloadLib()" functions which load the library, look
up the symbols, etc. The header function actually calls this one, but
you could easily modify it to toss out declarations for a static library
as well.
Enjoy! Big thanks to Ary for Descent's CTFE debugger; this took 45
minutes instead of 3 hours thanks to that!
(License : Public Domain)
#ifndef _PLATIF_H
#define _PLATIF_H
/**
* This file looks so weird because it's designed to be processed both
* by C and D. The C version is #included as-is. The D version is
* passed through a basic processor that expects this file to be
* in a VERY rigid format.
*
* D Preprocessor spec:
* - Everything outside @ blocks is ignored
* - Everything line an @COPY block is copied directly (except type translation)
* - Everything line in an @FUNCTIONS block is expected to be of the form
* "FUN(<name>, <return>, <params>);"
*/
#ifdef _MSC_VER
#define _LINKAGE extern "C" __declspec(dllexport)
#else
#define _LINKAGE extern "C"
#endif
#define FUN(__NAME__, __RV__, __PARAMS__) _LINKAGE __RV__ __NAME__ __PARAMS__
#define _CONST const
#define _VOID void
#define _LINT long // int masquerading as a long
#define _LUINT unsigned long // unsigned int masquerading as an unsigned long
#define _INT int
#define _UINT unsigned int
#define _LONG __int64
#define _ULONG unsigned __int64
#define _CHAR char
//@COPY
struct MofIf
{
// MOF streams -- first variable is a pointer to to a MofStream
structure
_INT (*Stream_New)(_VOID**);
_INT (*Stream_Read)(_VOID*, _VOID*, _LUINT, _LUINT*);
_INT (*Stream_Seek)(_VOID*, _ULONG);
_INT (*Stream_Tell)(_VOID*, _ULONG*);
_INT (*Stream_GetSize)(_VOID*, _ULONG*);
_INT (*Stream_GetAvailable)(_VOID*, _ULONG*);
};
//@END
//@FUNCTIONS
FUN(initMof, _VOID, (MofIf* _mof));
FUN(mimeOnFire, _INT, (_CONST _CHAR* path));
//@END
#undef FUN
#undef _LINKAGE
#undef _CONST
#undef _VOID
#undef _INT
#undef _UINT
#undef _INTL
#undef _UINTL
#undef _LONG
#undef _ULONG
#undef _CHAR
#endif
module mime.util.sharedlib;
public import tango.sys.SharedLib;
import mime.util.meta;
version(Windows) public const char[] SHARED_LIB_EXT = ".dll";
else version(Posix) public const char[] SHARED_LIB_EXT = ".so";
else static assert(false);
public char[] shared_mixin(char[] libFile, char[][] functions)
{
assert(functions.length % 2 == 0);
char[] r = "private SharedLib _lib;";
int numFuncs = functions.length / 2;
for(int i = 0; i < numFuncs; i++)
{
char[] type = functions[i * 2];
char[] name = functions[i * 2 + 1];
r ~= "public static " ~ type ~ " " ~ name ~ ";";
}
r ~= "public void _loadLib() {if(_lib)return;
scope(failure){_unloadLib();}";
r ~= "_lib = SharedLib.load(\"" ~ libFile ~ "\");";
for(int i = 0; i < numFuncs; i++)
{
char[] name = functions[i * 2 + 1];
r ~= name ~ " = " ~ "cast(typeof(" ~ name ~ "))
_lib.getSymbol(\"" ~ name ~ "\");";
}
r ~= "}";
r ~= "public void _unloadLib() { if(_lib){_lib.unload();}";
for(int i = 0; i < numFuncs; i++)
{
char[] name = functions[i * 2 + 1];
r ~= name ~ "=null;";
}
r ~= "}";
return r;
}
private char[] convertCppTypes(char[] line)
{
line = line.ctfeReplace("_VOID", "void");
line = line.ctfeReplace("_LUINT", "uint");
line = line.ctfeReplace("_LINT", "int");
line = line.ctfeReplace("_INT", "int");
line = line.ctfeReplace("_UINT", "uint");
line = line.ctfeReplace("_LONG", "long");
line = line.ctfeReplace("_ULONG", "ulong");
line = line.ctfeReplace("_CHAR", "char");
line = line.ctfeReplace("_CONST", "");
return line;
}
public char[] sharedFromHeader_mixin(char[] headerFile)(char[] libFile)
{
char[] h = import(headerFile);
int lineStart = 0;
bool inCopy = false;
bool inFunctions = false;
char[] r;
char[][] functions;
for(int i = 0; i < h.length; i++)
{
if(h[i] == '\n')
{
char[] line = h[lineStart .. i];
lineStart = i + 1;
if(line.ctfeStartsWith("//@"))
{
if(line.ctfeStartsWith("//@END"))
{
inCopy = false;
inFunctions = false;
}
else if(line.ctfeStartsWith("//@FUNCTIONS"))
inFunctions = true;
else if(line.ctfeStartsWith("//@COPY"))
inCopy = true;
}
else if(inCopy)
{
r ~= convertCppTypes(line);
}
else if(inFunctions)
{
if(!line.ctfeStartsWith("FUN("))
continue;
int s = 4;
int j;
for(j = 4; j < line.length; j++)
if(line[j] == ',')
break;
if (j == line.length)
continue;
char[] fname = line[s .. j];
s = j + 1;
for(j = j + 1; j < line.length; j++)
if(line[j] == ',')
break;
if (j == line.length)
continue;
char[] retTy = line[s .. j];
s = j + 1;
for(j = j + 1; j < line.length; j++)
if(line[j] == ';')
break;
if(line[j - 1] != ')')
continue;
char[] params = line[s .. j - 1];
functions ~= convertCppTypes(retTy ~ " function
" ~ params);
functions ~= fname;
}
}
}
return r ~ shared_mixin(libFile, functions);
}
/**
* Mime on Fire (mime) -- Simple UPnP server for XBOX360
* Copyright (C) 2009 Robert Fraser
*
* This program is free software; you can redistribute it andor
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
module mime.util.meta;
public char[] ctfeItoa(int n)
{
bool neg = false;
char[] r;
if (n < 0)
{
n = -n;
neg = true;
}
do
{
r = cast(char)('0' + (n % 10)) ~ r;
} while ((n /= 10) > 0);
if(neg)
r = '-' ~ r;
return r;
}
public bool ctfeStartsWith(char[] haystack, char[] needle)
{
for(int i = 0; i < needle.length; i++)
{
if(i >= haystack.length || haystack[i] != needle[i])
return false;
}
return true;
}
public char[] ctfeReplace(char[] text, char[] search, char[] rep)
{
char[] r;
int slen = search.length;
int tlen = text.length - slen + 1;
int lendiff = rep.length - slen;
int i = 0;
int j = 0;
for(i = 0; i < tlen; i++)
{
if(text[i .. i + slen] == search)
{
r ~= text[j .. i] ~ rep;
i = j = i + slen;
}
}
r ~= text[j .. $];
return r;
}
struct MofIf
{
// MOF streams -- first variable is a pointer to to a MofStream
structure
int (*Stream_New)(void**);
int (*Stream_Read)(void*, void*, uintL, uintL*);
int (*Stream_Seek)(void*, ulong);
int (*Stream_Tell)(void*, ulong*);
int (*Stream_GetSize)(void*, ulong*);
int (*Stream_GetAvailable)(void*, ulong*);
};
private SharedLib _lib;
public static void function(MofIf* _mof) initMof;
public static int function(char* path) mimeOnFire;
public void _loadLib()
{
if(_lib)
return;
scope(failure)
{
_unloadLib();
}
_lib = SharedLib.load("platif-d.dll");
initMof = cast(typeof(initMof)) _lib.getSymbol("initMof");
mimeOnFire = cast(typeof(mimeOnFire)) _lib.getSymbol("mimeOnFire");
}
public void _unloadLib()
{
if(_lib)
{
_lib.unload();
}
initMof = null;
mimeOnFire = null;
}
module mime.platinum.platif;
import mime.util.sharedlib;
debug private const char[] LIB_NAME = "platif-d" ~ SHARED_LIB_EXT;
else private const char[] LIB_NAME = "platif" ~ SHARED_LIB_EXT;
mixin(sharedFromHeader_mixin!("platif.h")(LIB_NAME));