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));

Reply via email to