Get Dll functions at compile time

Johnson Jones via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Aug 8 19:15:43 PDT 2017


On Wednesday, 9 August 2017 at 02:11:13 UTC, Johnson Jones wrote:
> I like to create code that automates much of the manual labor 
> that we, as programmers, are generally forced to do. D 
> generally makes much of this work automatable. For example, I 
> have created the following code which makes loading dlls 
> similar to libs:
>
>
>
> /* Import DLL functions in to type T. The following example 
> shows methodology
> struct DLLImports
> {
> 	@("DLLImport") public static extern(Windows)
> 	{
> 		@("libgdk-3-0.dll")
> 		{
> 			void* function(GdkWindow *window) 
> gdk_win32_window_get_handle;
> 		}
> 	}
> }
>
> // Fixes static functions and function pointers to point to 
> their specified DLL's
> DllImport!DLLImports;
> */
> void DLLImport(alias T)()
> {
> 	version(Windows)
> 	{
> 		import core.sys.windows.windows, std.conv, std.meta, 
> std.traits;
> 		HINSTANCE[string] DLLs;
> 	
> 		foreach(fname; __traits(allMembers, T))
> 		{	
> 			mixin("enum isf = isFunction!(T."~fname~");");
> 			mixin("enum isfp = isFunctionPointer!(T."~fname~");");
> 			mixin("enum attrs = __traits(getAttributes, 
> T."~fname~");");		
>
> 			static if ((isf || isfp) && attrs.length == 2 && attrs[0] == 
> "DLLImport")
> 			{
> 				auto dllName = attrs[1];
> 				if (dllName !in DLLs)
> 					DLLs[dllName] = LoadLibrary(to!wstring(dllName~"\0").ptr);
>
> 				auto dll = DLLs[dllName];
> 				if (dll == null)
> 					assert(0, "Cannot load DLL `"~dllName~"'");
>
> 				auto func = GetProcAddress(dll, fname);
>
> 				if (isf)
> 					mixin("auto p = cast(void**)&"~T.stringof~"."~fname~"; *p 
> = cast(typeof(p))func;");
> 				else		
> 					mixin(""~T.stringof~"."~fname~" = 
> cast(typeof("~T.stringof~"."~fname~"))func;");	
> 			}
> 		
> 		}
> 	}
>
>
>
> But this got me thinking that we don't even need to have to 
> specify the function in D, hell, they already exist in the lib 
> and we are just duplicating work.
>
> What if, at compile time, D could get all the functions and 
> their type information and build a class for them for us? We 
> could then just write something like
>
> struct DLLImports
> {
> 	@("DLLImport") string libgdk = "libgdk-3-0.dll";
> }
>
>
> and have some ctfe meta functions extract all the function from 
> libgdk and insert them in to the struct.
>
> There are two problems with this, one easy and one 
> hard/impossible(which would be easy if people were intelligent 
> enough to have foresight):
>
>
> 1. Get the dll function by name from the dll at compile time. 
> This would probably require manually reading the dll file and 
> scanning for the function.
>
> 2. Get the type information to build a declaration. This is 
> probably impossible since dll's do not contain the type 
> information about their parameters and return type(or do 
> they?). If they did, it would be easy. I would suggest that all 
> dll's generated by D include this information somewhere and an 
> easy way to extract it for future programmers so such things 
> could be implemented.
>
> Alternatively, maybe a master database could be queried for 
> such information by using the function names and dll name? I 
> don't know if D has network capabilities at compile time though.
>
> Alternatively, completely scrap the lethargic way things are 
> done in the name of backwards compatibility and $$$ and do 
> things right(learn from the past, stop repeating same mistakes, 
> etc). Sure it's a lot of work, but in the end is far less than 
> one thinks considering the increased productivity... but I 
> guess the we gotta keep buying the kids christmas presents.



And while we are at it, here is a set of meta functions that make 
using glade files easier:

// Loads the glade interface element id's and types directly from 
the glade interface file so we do not have to declare them 
manually
// Use: @("Glade") { 
mixin(DeclareGladeInterfaceElements!gladeFile); } where ever 
variables will be defined and InstantiateGladeInterfaceElements( 
to initalize them
public string DeclareGladeInterfaceElements(string filename)()
{
	auto token1 = "<object class=\"";
	auto token2 = " id=\"";
	string res = "";
	import std.file;
	auto data = import(filename);
	for(int i = 0; i < data.length; i++)
	{
		// Matched class/object type, get name actual type then get id 
if it has one.
		if (i+token1.length < data.length && data[i..i+token1.length] 
== token1)
		{
			i += token1.length;
			auto pos = i;
			while(i < data.length && data[i] != '"') i++;
			auto type = cast(string)data[pos..i];

			// Match id if it has one.
			while(i+token2.length < data.length && data[i] != '>' && 
data[i] != '<' && data[i] != '"' && data[i..i+token2.length] != 
token2) i++;
			i++;

			if (data[i] == '>') { continue; }
			i += token2.length;
			pos = i;
			if (data[i] == '>') { continue; }
			while(i+1 < data.length && data[i] != '"') i++;
			res ~= "public gtk."~type[3..$] ~ " " ~ data[pos..i] ~ 
";\n";			
		}

		if (i > data.length - 7130)
		{
			continue;
		}
	}
	return res;
}

public void InstantiateGladeInterfaceElements(T)(Builder builder, 
T t)
{
	import std.meta;		
	foreach(m; __traits(allMembers, T))
	{
		mixin("enum w = __traits(getAttributes, T."~m~");");		
		static if (w.length > 0 && w[0] == "Glade")
		{
			mixin("t."~m~" = 
(cast(typeof(T."~m~"))builder.getObject(`"~m~"`));");
			mixin("if (t."~m~" is null) { error(\"Error: `"~m~"` not 
found, User Interface Corrupted, Cannot continue!\"); }");
		}
	}
}

They automatically extract objects that have ID's with them, 
add/declare them in D and instantiate them. This means one 
doesn't have to duplicate code.

(DeclareGladeInterfaceElements could probably be optimized using 
proper D code but I just hacked something other)

Mainly posted for posterity and it might be useful to a few 
others, but is in line with using D's meta capabilities to 
generate D code at compile time from external sources so less 
keystrokes are required.






More information about the Digitalmars-d-learn mailing list