DLL library (Was: [boost] Re: additional boost libraries?)

Hi Sven,
Probably, you could start by explaining your vision of the library first. Of three points you've given:
The problems I would like to solve for myself are:
1) Often you need to write progams that need to work on different versions of a particular OS, with later versions having additional API calls that you could use.
void MyAPIStub() { static imported_function<void()> apiFunc( "ApiFunc", "OSLib" );
if ( !apiFunc ) { // do something here }
apiFunc();
I think this is reasonable use case. I wanted something similiar recently. I have a library which calls user-provided 'userinit' function. To allow the user to not provide the function, the library has its own definition. So, when the library's version is called, it call 'dlsym' to find the 'next' definition of 'userinit' and if it exists, calls it. But I'm afraid this semantic (find the next definition of symbol) is Unix-specific. I'm not even sure if two symbols with the same names in different DLLs are allowed on Windows.
Altough I acknowlege that the locking required in multi-threaded progams would clutter the syntax (more than) a little.
I'm not sure it's necessary, unless the called function is not thread-safe.
2) When creating plugin interfaces, having to go through extern "C" is a bit ugly to me. At the same time it ditches the C++ type safety. Being able to export a C++ plugin function and have the import library figure out the name mangling for me would improve the both the readability and the correctness of the program
I don't care that much about type safety of a single function, given that implementing that type-safety can be hard. But, of course, this would be a nice addition if you want to code it ;-) I'm more interested in nice interface to declare plugins. For example, on Unix static initialisers in a library are run when it's opened, so you can do something like this: extern std::map<std::string, Codegen*> codegens; // defined somewhere in application class Codegen1 {}; class Codegen2 {}; RegisterHelper<Codegen1> X(codegens, "1"); RegisterHelper<Codegen1> X(codegens, "2"); But will this work for Windows? Can DLL have unresolved external references? Are static initializers run when DLL is opened? If not, we'd have to do much less nice: void init_codegens(std::map<....>& m) { m["1"] = new Codegen1; m["2"] = new Codegen2; } It's less nice, because the first approach allows to register different codegens in different translation units. BTW, I think if you want typesafety, not don't need overloading, you can do this: // caller typedef void (right_type)(int, foo, bar); void* fp = ... get raw pointer to function ...; ((right_type*)fp)(....., typeid(right_type).name()); // callee typedef void (right_type)(int, foo, bar); void func(int, foo, bar, char* sig) { assert(strcmp(sig, typeid(right_type).name()) == 0); ..... }
boost::function<void (void)> f = static_cast<void (*void)>dlsym(h, "whatever")
- Volodya
Yeah, yesterday morning I was already thinking more of something like this
boost::function<void()> f1 = import<void()>( "lib", "whatever" ); boost::function<void()> f2 = import<void()>( lib, "whatever" ); boost::function<void()> f3 = import<void()>( lib, "whatever", some_decorator () );
Yes, I think that's just as fine. I'm a bit suspicious about passing library name as string -- you'd need to keep a list of all opened dll handles somewhere. Maybe just class 'dll' with method 'import'?
As far as the cdecl decorator and the tool is concerned. I have always considered it very likely that I would fail and not achieve what I want, but that won't stop me from trying.
;-) Just keep us informed somehow... I'd be interested in working on this too, though won't have any time till September. - Volodya

Vladimir Prus <ghost@cs.msu.su> writes:
1) Often you need to write progams that need to work on different versions of a particular OS, with later versions having additional API calls that you could use.
void MyAPIStub() { static imported_function<void()> apiFunc( "ApiFunc", "OSLib" );
if ( !apiFunc ) { // do something here }
apiFunc();
I think this is reasonable use case. I wanted something similiar recently. I have a library which calls user-provided 'userinit' function. To allow the user to not provide the function, the library has its own definition. So, when the library's version is called, it call 'dlsym' to find the 'next' definition of 'userinit' and if it exists, calls it.
But I'm afraid this semantic (find the next definition of symbol) is Unix-specific. I'm not even sure if two symbols with the same names in different DLLs are allowed on Windows.
On Windows the symbols from each DLL are entirely independent. However, you can specify at link time that certain symbols resolve to those from specific DLLs --- e.g. C++ standard library synbols resolve to the C++ runtime DLL. If you try and import the same symbol from two DLLs at link time, then there is a name clash. At runtime, you can get the address of a function in a DLL if you know the name of the function (or its ordinal number) and the name of the DLL. This allows you to have the same function implemented in many DLLs (e.g. application plug-ins which all export the same names for use by the application). There is no concept of "next definition" --- you have to explicitly specify the DLL to search for the symbol.
I'm more interested in nice interface to declare plugins. For example, on Unix static initialisers in a library are run when it's opened, so you can do something like this:
extern std::map<std::string, Codegen*> codegens; // defined somewhere in application
class Codegen1 {}; class Codegen2 {};
RegisterHelper<Codegen1> X(codegens, "1"); RegisterHelper<Codegen1> X(codegens, "2");
But will this work for Windows? Can DLL have unresolved external references? Are static initializers run when DLL is opened?
When a DLL is opened, Windows will implicitly call the exported initialization function. Whether or not your compiler uses this to run static initializers is up to the compiler vendor. You cannot have "unresolved" external references under Windows. You must specify that a given symbol is found in a given DLL either at link time or at runtime, as described above. If you want to have a "codegens" variable used by all the DLLs, then you probably ought to put it in a specific shared DLL, which is then referenced by all the others. e.g. codegens.dll --- contains definition of codegens from above mainapp.exe --- uses codegens from codegens.dll plugin1.dll --- references codegens from codegens.dll plugin2.dll --- references codegens from codegens.dll HTH, Anthony -- Anthony Williams Senior Software Engineer, Beran Instruments Ltd.

Anthony Williams wrote:
I think this is reasonable use case. I wanted something similiar recently. I have a library which calls user-provided 'userinit' function. To allow the user to not provide the function, the library has its own definition. So, when the library's version is called, it call 'dlsym' to find the 'next' definition of 'userinit' and if it exists, calls it.
But I'm afraid this semantic (find the next definition of symbol) is Unix-specific. I'm not even sure if two symbols with the same names in different DLLs are allowed on Windows.
On Windows the symbols from each DLL are entirely independent. However, you can specify at link time that certain symbols resolve to those from specific DLLs --- e.g. C++ standard library synbols resolve to the C++ runtime DLL. If you try and import the same symbol from two DLLs at link time, then there is a name clash.
Do you mean that if two DLL define two identically named symbols (and that symbol is present in both import libraries), then linking to both import libraries is still OK. What mechanism is use to select one of the symbols?
There is no concept of "next definition" --- you have to explicitly specify the DLL to search for the symbol.
Ok, noted.
But will this work for Windows? Can DLL have unresolved external references? Are static initializers run when DLL is opened?
When a DLL is opened, Windows will implicitly call the exported initialization function. Whether or not your compiler uses this to run static initializers is up to the compiler vendor.
Understood. What happens in practice, say on VC?
You cannot have "unresolved" external references under Windows. You must specify that a given symbol is found in a given DLL either at link time or at runtime, as described above.
If you want to have a "codegens" variable used by all the DLLs, then you probably ought to put it in a specific shared DLL, which is then referenced by all the others.
e.g.
codegens.dll --- contains definition of codegens from above
Ok, I think this is troublesome, but maybe possible to use. - Volodya

Sorry for breaking into this discussion.
When a DLL is opened, Windows will implicitly call the exported initialization function. Whether or not your compiler uses this to run static initializers is up to the compiler vendor.
Understood. What happens in practice, say on VC?
This is the responsibility of the C-Runtime library. The static constructor list is getting called from the _DllMainCRTStartup which is the "true" entry point of the DLL. DllMain is getting called then in turn from within _DllMainCRTStartup. ( Please also note that DllMain is called BEFORE the runtime library has been initialized!) Roland

Vladimir Prus <ghost@cs.msu.su> writes:
Anthony Williams wrote:
I think this is reasonable use case. I wanted something similiar recently. I have a library which calls user-provided 'userinit' function. To allow the user to not provide the function, the library has its own definition. So, when the library's version is called, it call 'dlsym' to find the 'next' definition of 'userinit' and if it exists, calls it.
But I'm afraid this semantic (find the next definition of symbol) is Unix-specific. I'm not even sure if two symbols with the same names in different DLLs are allowed on Windows.
On Windows the symbols from each DLL are entirely independent. However, you can specify at link time that certain symbols resolve to those from specific DLLs --- e.g. C++ standard library synbols resolve to the C++ runtime DLL. If you try and import the same symbol from two DLLs at link time, then there is a name clash.
Do you mean that if two DLL define two identically named symbols (and that symbol is present in both import libraries), then linking to both import libraries is still OK. What mechanism is use to select one of the symbols?
No. You generally specify to directly import symbols from specific DLLs at link time using their import libraries, but if two import libraries specify the same symbols you can't do that. With MSVC, you can generate an import library for one of the DLLs which omits one or more symbols. This can be done using the /DEF option of the LIB utility. Only the specified symbols are then imported from that DLL, and the linker can resolve other calls against another DLL. There may be other ways of doing this.
But will this work for Windows? Can DLL have unresolved external references? Are static initializers run when DLL is opened?
When a DLL is opened, Windows will implicitly call the exported initialization function. Whether or not your compiler uses this to run static initializers is up to the compiler vendor.
Understood. What happens in practice, say on VC?
VC runs the static initializers correctly for DLLs. Anthony -- Anthony Williams Senior Software Engineer, Beran Instruments Ltd.

On Fri, 23 Jul 2004 13:49:22 +0400, Vladimir Prus <ghost@cs.msu.su> wrote:
static imported_function<void()> apiFunc( "ApiFunc", "OSLib" );
Couldn't find a nice point in the thread at which to insert this comment, so I'll just spit it out: Since we have both the name of the function and the function type, and given that a lib such as this should provide a platform-independent interface, would it be useful if the library automatically mangled the name according to the type? Pros: * Type mismatches in arguments etc will be caught. * Possible to export multiple overloads of the same function name. * In the event that the imported library cannot be changed to use extern "C", it is still possible to import the function without resorting to platform-specific mangling in the function name string. Cons: * May cause problems if multiple mangling schemes exist on a single (OS) platform. * Not all mangling schemes are documented well or provide an API to perform the mangling. * Some (most likely insignificant) run-time and code-size overhead. /Mattias

"Mattias Flodin" wrote:
...would it be useful if the library automatically mangled the name according to the type?
It would. I remember there was attempt to create demangling library for Boost (based on libcwd?) but it wasn't finished. It was discussed in quite long threads here.
* May cause problems if multiple mangling schemes exist on a single (OS) platform.
These definitely exist (e.g. between Borland and VC). /Pavel

Hi Mattias,
On Fri, 23 Jul 2004 13:49:22 +0400, Vladimir Prus <ghost@cs.msu.su> wrote:
static imported_function<void()> apiFunc( "ApiFunc", "OSLib" );
Couldn't find a nice point in the thread at which to insert this comment, so I'll just spit it out: Since we have both the name of the function and the function type, and given that a lib such as this should provide a platform-independent interface, would it be useful if the library automatically mangled the name according to the type?
I think you've missed something ;-) Even my initial reply inthe "additional boost libraries" thread mentioned the possibility of calling functions which are not "extern "C". At least two other persons seems to want this. I find this would be nice, though not critical at all.
Pros: * Type mismatches in arguments etc will be caught. * Possible to export multiple overloads of the same function name. * In the event that the imported library cannot be changed to use extern "C", it is still possible to import the function without resorting to platform-specific mangling in the function name string.
Cons: * May cause problems if multiple mangling schemes exist on a single (OS) platform.
That's the case for gcc. Name mangling has changed in 3.x series. It should be possible to work around, though, for example by mangling the name twice and trying both mangled names. - Volodya

In article <200407260954.51051.ghost@cs.msu.su>, Vladimir Prus <ghost@cs.msu.su> wrote:
That's the case for gcc. Name mangling has changed in 3.x series. It should be possible to work around, though, for example by mangling the name twice and trying both mangled names.
CW has at least three name mangling schemes: - The old scheme (ARM-based, predates C++ ABI spec, used by every version of CW when building PEF executables) - The buggy new scheme (C++ ABI spec with some bugs, used by CW8 when building Mach-O executables) - The improved new scheme (C++ ABI spec, I am not aware of any bugs, used by CW9 when building Mach-O executables). I wouldn't bring up the bugs except that one of them has to do with mangling const char* parameters, so it comes up in practically every C++ library. Oops. Why yes, I did find this the hard way. meeroh

Vladimir Prus wrote:
Hi Mattias,
On Fri, 23 Jul 2004 13:49:22 +0400, Vladimir Prus <ghost@cs.msu.su> wrote:
static imported_function<void()> apiFunc( "ApiFunc", "OSLib" );
Couldn't find a nice point in the thread at which to insert this comment, so I'll just spit it out: Since we have both the name of the function and the function type, and given that a lib such as this should provide a platform-independent interface, would it be useful if the library automatically mangled the name according to the type?
I think you've missed something ;-) Even my initial reply inthe "additional boost libraries" thread mentioned the possibility of calling functions which are not "extern "C". At least two other persons seems to want this. I find this would be nice, though not critical at all.
At this point, I'm satisfied if the one who implements this library allows "extern C" functions only - perhaps for the non-extern C ones, in a future version. (otherwise, I think it would take too much to implement) Best, John -- John Torjo Freelancer -- john@torjo.com Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ Professional Logging Solution for FREE -- http://www.torjo.com/code/logging.zip (logging - C++) -- http://www.torjo.com/logview/ (viewing/filtering - Win32) -- http://www.torjo.com/logbreak/ (debugging - Win32) (source code available)

I think you've missed something ;-) Even my initial reply inthe "additional boost libraries" thread mentioned the possibility of calling functions which are not "extern "C". At least two other persons seems to want this. I find this would be nice, though not critical at all.
My apologies, it's difficult to keep up with boost and I seem to have skimmed over that part. It gets yet more confusing when the thread is split multiple times due to topic changes. ;) I agree that it's not critical though, and as I think I indicated, I'm not even sure if it's a good thing, but it's definitely worth thinking about the merits and the costs. /Mattias
participants (7)
-
Anthony Williams
-
John Torjo
-
Mattias Flodin
-
Miro Jurisic
-
Pavel Vozenilek
-
Roland
-
Vladimir Prus