Linking OnClick from data <xml>

I have created a small xml parser/encoder library that I call smollXML.
Both the server program (the one generating the xml data) and the client program must #include <smollXML.h> in order to use it.

Both server and client are in charge of maintaining the linkages in their build of smollXML, which is a unordered_map <std::string funcName, void (*)(item *)> callbackList;. (item is the base object of my oop system.) They both call a function void registerCallback(std::string name, void (*callback)(item*)); before saving/loading data.

Now, what I would like is to be able for smollXML on inclusion to both systems to be able to detect if functions are available. If it's obvious that most clients and servers are going to need OnClick for instance, it would be very convenient to provide for that.

Is there a way to check if a function exists either at compile time or even better, at run-time?
Last edited on
Is there a way to check if a function exists either at compile time or even better, at run-time?


At runtime, using the dynamic linker:
https://man7.org/linux/man-pages/man3/dlsym.3.html
https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress

At compile-time, using SFINAE or a requires clause.
Last edited on
OK. I was stunned how fast you posted that answer, I refreshed about a half hour later to find you had the answer posted in under 3 minutes.

I'm putting SFINAE in my back pocket to use later, that's pretty cool.

The current path I'm looking into is to see if I could use SDL_LoadObject(), I know that it uses dlsym as it's Unix/Linux implementation.

I will post back if I have success.
I really wanted dlsym to be the answer, but I came across the weak attribute for GCC and #pragma weak for windows. This is a slightly better match for what I'm trying to do. (bazooka vs fly-swatter situation)

However, I did not know that there was a way to use dlsym on executables, so I will continue reading into that, it would be interesting as a way to implement reflection.

Here's my test for the weak attribute in GCC. I tried to make it cross platform for Windows, but I don't own a windows machine to test it against. Let me know if you test it and it works or not.
Un-comment the "func" function and it will be loaded and run properly, otherwise the function is properly ignored.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <unordered_map>
#include <string>

class item
{
public:
	item();
	void callee();
	void (*call)(item *);
};

item::item()
{
	call = NULL;
}

void item::callee()
{
	std::cout << "Calling member function callee\n";
}


void local(item * owner)
{
	std::cout << "Calling local function\n";
	owner->callee();
}

#pragma weak func
#ifdef WIN32
void func(item *);
#else
void func(item *) __attribute((weak));
#endif

/*
void func(item * thing)
{
	std::cout << "Called fun\n";
	if(thing)
	{
		thing->callee();
	}
}
*/

int main()
{
	// instructions would be loaded by xml parser
	std::string instructions = "onClick";
	// reg is managed by smollXML document class
	// bobby would technically be a new item pointer populated and returned by doc.getObj(std::string name); 
	std::unordered_map <std::string, void (*)(item *)> reg;

	if(func)
	{
		std::cout << "Has func\n";
		reg["onClick"] = func;
	}
	else
	{
		reg["onClick"] = local;
	}

	item bobby;
	bobby.call = reg[instructions];
	bobby.call = reg["onClick"];

	if(bobby.call)
	{
		bobby.call(&bobby);
	}
}


Thank you Mbozzi, you put me on the right path, and you have given me some really good topics to read up on.
That weird part of my brain that likes recursive functions feels like it wants to sink it's teeth into dlsym right now.
[edit: This may not be the true solution, I still have to try it in it's own header which may cause further headaches. If I want this to work then I may have to implement an init() function, but I think this will still be worth the extra work.]
Last edited on
Glad that helped! But I admit I don't understand what you're doing or how this plays into it. Is it about allowing mismatched versions to interoperate?

What you're doing with reg here reminds me of "procedure linkage tables".

The PLT is a table of function pointers mapped by function names, like reg in your code. At first each function pointer is initialized to a stub function, that calls into the runtime linker. The runtime linker looks up the real address of the function the pointer was associated with in the table, and overwrites that pointer with the new address. Then it calls the newly-found function.

I don't own a windows machine to test it against

MSVC doesn't understand #pragma weak, but there is an undocumented linker flag that apparently can be used to do the same thing:
https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024
Last edited on
Sorry about the above code, it's my proof of concept before I add it into my larger framework so it may be lacking in clarity.

I gave myself the challenge this week of creating a gui menubar that could be defined in xml such that
1
2
3
4
5
6
<menubar  "x=0" "y=0" "height=25" "width=fit to window">
    <menu "text=file" "onHover=menuHovered" "onClick=menuClicked">
      <option "id=0" "text=New" "onClick=newDocument">icon_newDoc.png</option>
      <option "id=1" "text=Quit" "onClick=quitProgram"></option>
    </menu>
</menubar>


My smollXML library needs the addresses of the linkable functions, and reg in the previous post stores pointers to functions with aliased names. The names in reg may or may not match the actual name of the function, but that is OK because the alias is what I have stored in the data. The tag <Option "id=1"> above is saying that the onClick callback that is owned by option 1 has an alias in smollXML::reg["quitProgram"]; but it is also the job of the client to define quitProgram and register it into reg. If all else fails, then the pointer is set to NULL when queried and the client must deal with that as well.

It would be interesting to access the PLT of the program from within the code, but since that resides in the linker this is what I'm doing in the absence of that functionality. Perhaps with linker scripts that kind of access might be possible, but that's way beyond my knowledge.

The question about detecting existence of functions was a bit of a distraction from the challenge, but along the same train of thought. I wanted to be able to guess at some common function names and provide the API to them with an init function should they actually exist in the client program. I am currently second-guessing the wisdom of that... It may cause confusion to a user as to why some functions are being pre-loaded, while others need to be manually added to the list.
Last edited on
Note that #pragma weak isn't supported by VS
seeplus wrote:
Note that #pragma weak isn't supported by VS

IIRC "#pragma anything" is gonna be defacto non-standard, no matter what. What works with one compiler suite is not guaranteed to work with others.

A semi-exception to that is #pragma once . Still not recognized by all compilers AFAIK, though.

There seem to be more than a few people who want to make #pragma once be added to the C++ standard, in part to get away from verbose #ifndef/#define/#endif header guards.

With modules that becomes a (somewhat) moot point, there is no need to worry about inadvertent multiple imports.

In my admittedly limited experience using modules, with VS 2019/2022*, a module interface file (.cppm) can import a C++ library, say import <random>;. Import that interface file into a translation unit that uses the <random> library and that translation unit needs to also import <random>; or compilation fails.

Modules are not header files in my experience. At least not with VS. *shrug*

*AFAIK there is no definitive standard for the file extension of module interface files yet, I've seen .cppm commonly used. MS (and VS) want to use .ixx. *Blech*
Topic archived. No new replies allowed.