www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - boilerplate generation

reply "Carl Sturtivant" <sturtivant gmail.com> writes:
I want to be able to define a number of extern(C) functions that 
will define a C API in a DLL, varying only in their names, each 
as follows.

export extern(C) int f( uint argc, A* argv) { return Wrapper!f( 
argc, argv); }

Here Wrapper!f is a wrapper around a D function named f that does 
the work, called from C via the f defined here.

I'd like a piece of D machinery that provides a short clean way 
to embed one of these in a program, where the name f is specified 
once, and the code is invisible. I've had no success at solving 
this problem with templates or mixin templates. I can use CTFE so 
I can write

mixin( Export("f"));

to introduce the definition for f. Is this the best way to 
proceed? What are other options?
Dec 29 2013
parent reply "Dicebot" <public dicebot.lv> writes:
Does this look clean enough? (Not sure I have completely 
understood the question)

/* export extern(C) int f( uint argc, A* argv) { return Wrapper!f(
argc, argv); } */

struct A {}

int Wrapper(alias func, T...)(T args) { return 42; }

template ExternC(alias existingFunc)
{
	import std.string : format;
	import std.traits : ReturnType;
	
	static assert(is(typeof(&existingFunc) : int function(uint, 
A*)));
	
	mixin(format(
		"export extern(C) int %s(uint argc, A* argv) { return 
Wrapper!(%s)(argc, argv); }",
		__traits(identifier, existingFunc),
		__traits(identifier, existingFunc)
	));
}

// to avoid name clash, assuming you have different module in 
real code
struct X
{	
	static int fff(uint, A*)
	{
		return 42;
	}
}

mixin ExternC!(X.fff);

pragma(msg, typeof(fff));
// extern (C) int(uint argc, A* argv)

void main()
{
}
Dec 29 2013
parent reply "Carl Sturtivant" <sturtivant gmail.com> writes:
On Sunday, 29 December 2013 at 22:01:19 UTC, Dicebot wrote:
 Does this look clean enough? (Not sure I have completely 
 understood the question)

 /* export extern(C) int f( uint argc, A* argv) { return 
 Wrapper!f(
 argc, argv); } */

 struct A {}

 int Wrapper(alias func, T...)(T args) { return 42; }

 template ExternC(alias existingFunc)
 {
 	import std.string : format;
 	import std.traits : ReturnType;
 	
 	static assert(is(typeof(&existingFunc) : int function(uint, 
 A*)));
 	
 	mixin(format(
 		"export extern(C) int %s(uint argc, A* argv) { return 
 Wrapper!(%s)(argc, argv); }",
 		__traits(identifier, existingFunc),
 		__traits(identifier, existingFunc)
 	));
 }

 // to avoid name clash, assuming you have different module in 
 real code
 struct X
 {	
 	static int fff(uint, A*)
 	{
 		return 42;
 	}
 }

 mixin ExternC!(X.fff);

 pragma(msg, typeof(fff));
 // extern (C) int(uint argc, A* argv)

 void main()
 {
 }
Very nice. Only one problem: when I use dmd/win32 to build the DLL, and analyze the resulting export, it's _D5mixin43__T7ExternCS28_D5mixin1X3fffFkPS5mixin1AZiZ3fffUkPS5mixin1AZi and not _fff or similar. So somehow the extern(C) hasn't led to a C symbol.
Dec 29 2013
parent reply "Dicebot" <public dicebot.lv> writes:
On Sunday, 29 December 2013 at 22:28:00 UTC, Carl Sturtivant 
wrote:
 Very nice. Only one problem: when I use dmd/win32 to build the
 DLL, and analyze the resulting export, it's
 _D5mixin43__T7ExternCS28_D5mixin1X3fffFkPS5mixin1AZiZ3fffUkPS5mixin1AZi
 and not _fff or similar. So somehow the extern(C) hasn't led to 
 a C symbol.
Ah, yeah, have missed that. Such thing is expected if symbol is nested and can't have clear C mangling (then extern(C) only impacts ABI). It is kind of weird that this is also the case for mixin templates as those are injected into target scope but makes sense once you remember that even mixin templates conform symbol hygiene. It can be forced by pragma(mangle) though. Updated code: struct A {} int Wrapper(alias func, T...)(T args) { return 42; } template ExternC(alias existingFunc) { import std.string : format; import std.traits : ReturnType; static assert(is(typeof(&existingFunc) : int function(uint, A*))); private enum name = __traits(identifier, existingFunc); mixin(format( q{pragma(mangle, "%s") export extern(C) int %s(uint argc, A* argv) { return Wrapper!(%s)(argc, argv); }}, name, name, name )); } // to avoid name clash, assuming you have different module in // real code struct X { static int fff(uint, A*) { return 42; } } mixin ExternC!(X.fff); pragma(msg, typeof(fff)); // extern (C) int(uint argc, A* argv) pragma(msg, fff.mangleof); // fff void main() {}
Dec 29 2013
parent reply "Carl Sturtivant" <sturtivant gmail.com> writes:
On Monday, 30 December 2013 at 04:35:34 UTC, Dicebot wrote:
 It can be forced by pragma(mangle) though. Updated code: [...]
Never heard of pragma(mangle)! Very useful. What role does it play in D? Is it guaranteed implemented on x86 or amd64? Thanks for the answer, very nice.
Dec 30 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Monday, 30 December 2013 at 17:33:13 UTC, Carl Sturtivant 
wrote:
 On Monday, 30 December 2013 at 04:35:34 UTC, Dicebot wrote:
 It can be forced by pragma(mangle) though. Updated code: [...]
Never heard of pragma(mangle)! Very useful. What role does it play in D? Is it guaranteed implemented on x86 or amd64? Thanks for the answer, very nice.
It's unrelated to the target architecture, it's just for naming. See dlang.org/abi.html
Dec 30 2013
parent reply "Carl Sturtivant" <sturtivant gmail.com> writes:
On Monday, 30 December 2013 at 17:46:57 UTC, John Colvin wrote:
 It's unrelated to the target architecture, it's just for 
 naming. See dlang.org/abi.html
Apologies, I was perhaps unclear. I'm just wondering if pragma(mangle) is guaranteed to be implemented, so I can safely put it in portable D. It's not mentioned in http://dlang.org/pragma.html
Dec 30 2013
parent "Dicebot" <public dicebot.lv> writes:
On Monday, 30 December 2013 at 20:04:00 UTC, Carl Sturtivant 
wrote:
 Apologies, I was perhaps unclear. I'm just wondering if 
 pragma(mangle) is guaranteed to be implemented, so I can safely 
 put it in portable D. It's not mentioned in 
 http://dlang.org/pragma.html
Nature of reference compiler is that anything implemented there is approved as part of spec. I guess one implementing it just forgot to add matching documentation entry as it is relatively new addition. https://d.puremagic.com/issues/show_bug.cgi?id=11846
Dec 30 2013