Self-registration in a library

K

Kaba

Hi,

Have a look at the following:

aFile.cpp
---------

#include <iostream>
#include <string>

namespace
{

void libraryAddFunction(
const std::string& name, void (*function)());

struct CallFunction
{
CallFunction(void (*callback)()) {callback();}
};

void aFunction()
{
std::cout << "aFunction" << std::endl;
}

void addFunction()
{
libraryAddFunction("aFunction", aFunction);
}

CallFunction call(addFunction);

}

register.cpp
------------

#include <string>
#include <map>

typedef std::map<std::string, void (*)()> FunctionMap;

FunctionMap& functionMap()
{
static FunctionMap theFunctionMap;
return theFunctionMap;
}

void libraryAddFunction(const std::string& name,
void (*function)())
{
functionMap().insert(
std::make_pair(name, function));
}

void runFunction(const std::string& name)
{
if (functionMap().count(name))
{
functionMap()[name]();
}
}

I then compile aFile.cpp and register.cpp into a static library, say
library.lib. Next I build the following trivial main along with the
library.lib:

main.cpp
--------

#include <string>

void runFunction(const std::string& name);

int main()
{
runFunction("aFunction");
return 0;
}

And the result is... That nothing is printed. The first question is: why
not? When all of the three files are built together, then the result is
the string "aFunction" printed on screen.

The idea here is that the aFile.cpp is kind of an independent component,
such that similar files could be added which register their
functionality automatically. The second question is: how to recover the
self-registration property in the library case?
 
J

James Kanze

[...]
I'd suggest you learn what a library is. If you don't tell the
compiler/linker somehow that your automatically registered file
is part of the program, it's not part of the program. How you
tell this to the translation system is system dependent, but by
definition, if the object file is in a library, you've told the
system to only include it if it resolves an otherwise unresolved
external. Which leads to...
Because your main() translation unit does not reference any undefined
symbols that are define in the aFile.cpp translation unit, so when you
statically link against it, the aFile.cpp translation unit is not added to
your binary.
This would work, as intended, if you build your module as a shared library,
and link against it.

That's because "shared libraries" aren't really libraries, in the
traditional sense; they behave like object files with regards to
linking. (Note that when they first showed up on Suns, a long
time ago, they were called "shared objects"---whence the
..so---not shared libraries, because of this. Calling them
libraries is a Microsoftism which has since invaded the Unix
world.)
This kind of a linkage issue is really platform specific. You did not
specify what platform you are using. This is not really a C++ question.
There is no such thing as a "library" in the C++ programming language.

That's actually not true; the standard very definitely talks
about libraries. And the fundamentals aren't platform specific
(since the behavior is the same on all platforms I've every seen
or heard of), even if they aren't C++ specific either: it all
goes back to the basic definition of a library.
Depending on your platform and C++ compiler, there may be compilation
switches that you can use to force the inclusion of a translation unit from
a statically-linked library.

The simplest (and the most portable) is probably to just link
the object file, rather than going through a library.
 
K

Kaba

James said:
Kaba said:
Have a look at the following:
[...]
And the result is... That nothing is printed. The first question is: why
not? When all of the three files are built together, then the result is
the string "aFunction" printed on screen.

I'd suggest you learn what a library is. If you don't tell the
compiler/linker somehow that your automatically registered file
is part of the program, it's not part of the program. How you
tell this to the translation system is system dependent, but by
definition, if the object file is in a library, you've told the
system to only include it if it resolves an otherwise unresolved
external. Which leads to...

Right. So a static library does not behave equivalent to a set of object
files.

I am targeting cross-platform, so portability is important to me.

James said:
The simplest (and the most portable) is probably to just link
the object file, rather than going through a library.

In this case I was specifically set out to avoid this. I am creating a
mex file (an "extension") to Matlab, and here the final build set has to
be invoked from within Matlab. What I want to do is to maximize the
amount of work done in my own build system, and then just do a trivial
build in Matlab. Hence my approach of first building a library. I want
to keep the library a static one, rather than shared.

To guarantee that aFile.cpp gets registered, I need to explicitly refer
to something in it. Is it enough to refer to a single name in a
translation unit (say call a function in it) to guarantee its inclusion,
or is the process even more fine grained? I.e. if I call a function in
it, am I guaranteed that the 'call' global variable in aFile.cpp gets
constructed?
 
R

red floyd

    [...]
And the result is... That nothing is printed. The first question is: why
not? When all of the three files are built together, then the result is
the string "aFunction" printed on screen.
I'd suggest you learn what a library is.  If you don't tell the
compiler/linker somehow that your automatically registered file
is part of the program, it's not part of the program.  How you
tell this to the translation system is system dependent, but by
definition, if the object file is in a library, you've told the
system to only include it if it resolves an otherwise unresolved
external.  Which leads to...

Right. So a static library does not behave equivalent to a set of object
files.

I am targeting cross-platform, so portability is important to me.

James said:
The simplest (and the most portable) is probably to just link
the object file, rather than going through a library.

In this case I was specifically set out to avoid this. I am creating a
mex file (an "extension") to Matlab, and here the final build set has to
be invoked from within Matlab. What I want to do is to maximize the
amount of work done in my own build system, and then just do a trivial
build in Matlab. Hence my approach of first building a library. I want
to keep the library a static one, rather than shared.

To guarantee that aFile.cpp gets registered, I need to explicitly refer
to something in it. Is it enough to refer to a single name in a
translation unit (say call a function in it) to guarantee its inclusion,
or is the process even more fine grained? I.e. if I call a function in
it, am I guaranteed that the 'call' global variable in aFile.cpp gets
constructed?


In you header, you could add something like:

static const bool somevar = somefunc_in_the_cpp_file();
 
K

Kaba

red said:
In you header, you could add something like:

static const bool somevar = somefunc_in_the_cpp_file();

I have now solved this as follows. Into each self-registering file x, I
add an empty function:

void force_linking_x() {}

Then in the main.cpp I do for each x:

#define FORCE_LINKING(name) \
void force_linking_##name(); \
force_linking_##name()

namespace
{

void forceLinking()
{
FORCE_LINKING(x);
FORCE_LINKING(y);
...
}

}

This works ok.
 
J

James Kanze

James said:
Kaba writes:
Have a look at the following:
[...]
And the result is... That nothing is printed. The first question is: why
not? When all of the three files are built together, then the result is
the string "aFunction" printed on screen.
I'd suggest you learn what a library is. If you don't tell the
compiler/linker somehow that your automatically registered file
is part of the program, it's not part of the program. How you
tell this to the translation system is system dependent, but by
definition, if the object file is in a library, you've told the
system to only include it if it resolves an otherwise unresolved
external. Which leads to...
Right. So a static library does not behave equivalent to a set of object
files.

A "library" is a library, not something else:). But I can
understand that there is some ambiguity. Historically, for
example, Unix used "archive" files as libraries, and an archive
file is just a set of files---except when the linker considers
it to be a library.

Anyway, the behavior of a library is a desirable, even
a necessary behavior, and it won't (hopefully) change. The
problem as I see it is that you don't want the behavior of
a library, so you have to use something else.
I am targeting cross-platform, so portability is important to me.
In this case I was specifically set out to avoid this. I am creating a
mex file (an "extension") to Matlab, and here the final build set has to
be invoked from within Matlab. What I want to do is to maximize the
amount of work done in my own build system, and then just do a trivial
build in Matlab. Hence my approach of first building a library. I want
to keep the library a static one, rather than shared.

I'm not sure I understand correctly. I'm not familiar with
Matlab, but all of the "extensions" I am familiar with work by
means of loading a DLL (or an so under Unix---basically the same
thing). You provide this DLL, and logically, I don't see why
you'd want to use static libraries anywhere. Static libraries
are really only useful when you want to deliver your code to
other C++ (or C) users, who will link against it.
To guarantee that aFile.cpp gets registered, I need to explicitly refer
to something in it. Is it enough to refer to a single name in a
translation unit (say call a function in it) to guarantee its inclusion,
or is the process even more fine grained? I.e. if I call a function in
it, am I guaranteed that the 'call' global variable in aFile.cpp gets
constructed?

In practice, yes. The granularity of all of the linkers I know
is the object file, at least by default. (VC++ has an option to
support granularity at the function level, but I think this is
more a space optimization: the true granularity is still the
object file, but the linker suppresses *functions* which aren't
referenced when it links the object file in.)

And you don't need to "call" a function; just include somewhere
a table which takes the address of a set of functions. Or
variables, or whatever. (If I do have to do something like
this, I'll do it automatically, from the makefile, or by using
a pre-build step in Visual. One doesn't actually write this
sort of code.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
VetaMcRae
Top