www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - pathologically simple hotloading

reply monkyyy <crazymonkyyy gmail.com> writes:
can anyone imagine any simplifications?

```d app.d
import std;
import core.stdc.stdlib;
import core.sys.posix.dlfcn;

struct enity{
	int i;
	ubyte[100] j;
	//int x;
	//int y;
}
string exe(string input){
	//auto config=0;//Config.stderrPassThrough|Config.retainStdout;
	//input.writeln;
	return input.executeShell(/*null,config*/).output[0..min($,$-1)];
}
void runcompiler(){
	"opend -shared -of=hotload.so hotload.d".exe.writeln;
}
void* handle;
void*[] store;
string[] api;
enum PATH=__FILE_FULL_PATH__[0..$-__FILE__.length];
void loadso(){
	handle=dlopen((PATH~"/hotload.so").toStringz,RTLD_LAZY);
	assert(handle!=null,dlerror.to!string);
	store=[];
	foreach(s;api){
		store~=dlsym(handle,s.toStringz);
	}
}
void hotload(string s,T...)(ref enity e,T args){
	alias F=extern(C) void function(enity*,T);
	auto I=api.countUntil(s);
	if(I==-1){return;}
	if(store[I]==null){return;}
	F call=cast(F)(store[I]);
	call(&e,args);
}
void main(){
	api~="foo";
	api~="bar";
	runcompiler;
	loadso;
	//---
	enity[10] enitys;
	foreach(ref e;enitys){
		hotload!"foo"(e);
	}
	foreach(e;enitys){
		e.i.writeln;
	}
	foreach(ref e;enitys){
		hotload!"bar"(e,true);
	}
}
```

```d hotload.d
import std;
extern(C):
struct thingy{
	int x;
	int y;
}
void foo(thingy* t){
	t.x=uniform(0,10);
	t.y=uniform(20,50);
	(*t).writeln;
}
void bar(thingy* t,bool b){
	(*t).writeln;
}
```

As written I believe ill need to improve my compiler bugs ulities 
to make a ct appendable string[]
i'll need to detect enity-like data types and ensure that

trade off between a uda based like my old version: 
https://codeberg.org/monkyyy/raylib2026/src/commit/e5a7e93cc379981ca44bab70f92f3dfd68653944/hotloadlib.d#L35

I think a void* version is more flexible and should have less 
compile time (as hotload.d isnt even processed) its changing the 
"direction" of the type inference so by not reading `hotload` 
maybe I avoid spooky order of complation issues in exchange for 
less safety?

idk, I feel like this is all under explored, how to best use 
dlsym with template based apis?
Jun 05
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Fri, Jun 05, 2026 at 08:18:58PM +0000, monkyyy via Digitalmars-d-learn wrote:
 can anyone imagine any simplifications?
What's the point of iterating the `store` array? Just use an AA and lookup the function name as string key, if null, fill it with dlsym. If still null, it doesn't exist, else call it right away. Instant on-demand function binding! --T
 ```d app.d
 import std;
 import core.stdc.stdlib;
 import core.sys.posix.dlfcn;
 
 struct enity{
 	int i;
 	ubyte[100] j;
 	//int x;
 	//int y;
 }
 string exe(string input){
 	//auto config=0;//Config.stderrPassThrough|Config.retainStdout;
 	//input.writeln;
 	return input.executeShell(/*null,config*/).output[0..min($,$-1)];
 }
 void runcompiler(){
 	"opend -shared -of=hotload.so hotload.d".exe.writeln;
 }
 void* handle;
 void*[] store;
 string[] api;
 enum PATH=__FILE_FULL_PATH__[0..$-__FILE__.length];
 void loadso(){
 	handle=dlopen((PATH~"/hotload.so").toStringz,RTLD_LAZY);
 	assert(handle!=null,dlerror.to!string);
 	store=[];
 	foreach(s;api){
 		store~=dlsym(handle,s.toStringz);
 	}
 }
 void hotload(string s,T...)(ref enity e,T args){
 	alias F=extern(C) void function(enity*,T);
 	auto I=api.countUntil(s);
 	if(I==-1){return;}
 	if(store[I]==null){return;}
 	F call=cast(F)(store[I]);
 	call(&e,args);
 }
 void main(){
 	api~="foo";
 	api~="bar";
 	runcompiler;
 	loadso;
 	//---
 	enity[10] enitys;
 	foreach(ref e;enitys){
 		hotload!"foo"(e);
 	}
 	foreach(e;enitys){
 		e.i.writeln;
 	}
 	foreach(ref e;enitys){
 		hotload!"bar"(e,true);
 	}
 }
 ```
 
 ```d hotload.d
 import std;
 extern(C):
 struct thingy{
 	int x;
 	int y;
 }
 void foo(thingy* t){
 	t.x=uniform(0,10);
 	t.y=uniform(20,50);
 	(*t).writeln;
 }
 void bar(thingy* t,bool b){
 	(*t).writeln;
 }
 ```
 
 As written I believe ill need to improve my compiler bugs ulities to make a
 ct appendable string[]
 i'll need to detect enity-like data types and ensure that
 
 trade off between a uda based like my old version:
https://codeberg.org/monkyyy/raylib2026/src/commit/e5a7e93cc379981ca44bab70f92f3dfd68653944/hotloadlib.d#L35
 
 I think a void* version is more flexible and should have less compile time
 (as hotload.d isnt even processed) its changing the "direction" of the type
 inference so by not reading `hotload` maybe I avoid spooky order of
 complation issues in exchange for less safety?
 
 idk, I feel like this is all under explored, how to best use dlsym with
 template based apis?
Jun 05
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Friday, 5 June 2026 at 23:46:12 UTC, H. S. Teoh wrote:
 On Fri, Jun 05, 2026 at 08:18:58PM +0000, monkyyy via 
 Digitalmars-d-learn wrote:
 can anyone imagine any simplifications?
What's the point of iterating the `store` array?
`.countUntil(s)` a stand in for a ct counter(at which point should compile out completely); I have not yet written it but I should be able to avoid ctfe-gc landmines to produce an appendable enum-ish string[]; making "api" constructed ct and synced with template memoization The ct counter is extermely well tested and trusted if I get the ct strings working and, critically, communicated correctly to loadso. Im slightly unsure about when "enum-copy-and-paste-into-executable" is done but I think I have a method for that, altho theres some complexity about strings as a specail case of value litterals
Jun 05
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 6 June 2026 at 00:11:39 UTC, monkyyy wrote:
 I have not yet written it but I should be able to avoid ctfe-gc 
 landmines to produce an appendable enum-ish string[];
my current progress on improving my ct utilities, this alone was a nightmere ```d import std; enum pointer=cast(immutable(void)*)[0].ptr; template changepointer(alias p){ enum changepointer=(){ *(cast(int*)pointer)+=*cast(int*)p; return 1; }(); } unittest{ (*(cast(int*)pointer)).writeln; enum immutable(void)* newpointer=cast(immutable(void)*)[5].ptr; enum _=changepointer!(newpointer); } ``` the ctfe gc is even more fickle then I remember, I think int and void is the special case I think I can make a reverse linked list given only mutable void* pointers but Im more open to suggestions if that fails then I would have to resort to appendable aliasseq of aliased strings and that **doesnt** have a clean way to communcate to post-compiletime that I know of I want void*[] to be my hotloading primitive type
Jun 07
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Sunday, 7 June 2026 at 21:38:23 UTC, monkyyy wrote:
 On Saturday, 6 June 2026 at 00:11:39 UTC, monkyyy wrote:
 I have not yet written it but I should be able to avoid 
 ctfe-gc landmines to produce an appendable enum-ish string[];
my current progress on improving my ct utilities, this alone was a nightmere ```d import std; enum pointer=cast(immutable(void)*)[0].ptr; template changepointer(alias p){ enum changepointer=(){ *(cast(int*)pointer)+=*cast(int*)p; return 1; }(); } unittest{ (*(cast(int*)pointer)).writeln; enum immutable(void)* newpointer=cast(immutable(void)*)[5].ptr; enum _=changepointer!(newpointer); } ``` the ctfe gc is even more fickle then I remember, I think int and void is the special case I think I can make a reverse linked list given only mutable void* pointers but Im more open to suggestions if that fails then I would have to resort to appendable aliasseq of aliased strings and that **doesnt** have a clean way to communcate to post-compiletime that I know of I want void*[] to be my hotloading primitive type
The if you manage to dodge all the safetys on the ctfe gc, you get segfualts or maybe the compiler gets confused and cant link a linked list of enums, idk; either way that direction was a dead end as far as I can tell, I believe static this spam maybe the lowest cost option for value ```d import std; int[] hi; template foo(alias a){ static this(){ hi~=a; } enum foo=0; } enum _1=foo!1; enum _2=foo!2; unittest{ hi.writeln; } ```
Jun 08
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
Thank you for doing this, I'm adding it to my list of references showing 
why we need linker lists.
Jun 08
parent monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 9 June 2026 at 00:03:37 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Thank you for doing this, I'm adding it to my list of 
 references showing why we need linker lists.
---
  Route("/path") // but actually is:  Route!myRoute("/path")
 void myRoute() {

 }

 void main() {
     import std.stdio;
     foreach(immutable(RouteInfo)* route; routes) {
         writeln(route.path);
     }
 }
Id suggest something more general, maybe enum slices are mutable compile time if they have a pragma: ```d pragma(ctmutablity) enum int[] foo; unittest{ foo.writeln;//1,2 } unittest{ foo~=1; //pragma(mgs,foo.stringof);//"error compile time appended enums are unsafe to access at compile time because we hate fun" } unittest{ foo~=2; } ``` ```d pragma(ctmutablity) void*[] foo; pragma(ctmutablity) void function()[] bar; void myroute(){} foo~=&myroute; ```
Jun 08