How to detect multiple lib versions?

B

Bob Altman

Hi all,

I have authored several native DLLs (call them A, B, and C). These
libraries have references between each other (A calls B which calls C).
These libraries are built and made available to callers both in "debug" and
"release" versions. The release versions of the libraries are A, B, and C
(.lib and .dll), while the debug versions are A_debug, B_debug, and C_debug
(.lib and .dll).

Now, because these libraries maintain internal state and call into each
other, ugly things happen if a caller links with the release version of one
library and the debug version of another library. At run-time, this causes
both versions of some of the libraries to be simultaneously loaded. (For
example, if the user calls into the debug version of A, which calls into the
debug version of B, and then the user explicitly calls into the release
version of B, then both B and B_debug would be simultaneously loaded, each
with its own internal state.)

So, how can I detect when a program references the debug version of one of
my libraries and the release version of another library? Ideally, I'd like
to put some magic into the source code that causes a linker error.
Alternatively, I guess I could have DLL initialization code that somehow
detects when more than one instance of the DLL have been loaded.

TIA - Bob
 
D

David Connet

Hi all,

I have authored several native DLLs (call them A, B, and C). These
libraries have references between each other (A calls B which calls
C). These libraries are built and made available to callers both in
"debug" and "release" versions. The release versions of the libraries
are A, B, and C (.lib and .dll), while the debug versions are A_debug,
B_debug, and C_debug (.lib and .dll).

Now, because these libraries maintain internal state and call into
each other, ugly things happen if a caller links with the release
version of one library and the debug version of another library. At
run-time, this causes both versions of some of the libraries to be
simultaneously loaded. (For example, if the user calls into the debug
version of A, which calls into the debug version of B, and then the
user explicitly calls into the release version of B, then both B and
B_debug would be simultaneously loaded, each with its own internal
state.)

So, how can I detect when a program references the debug version of
one of my libraries and the release version of another library?
Ideally, I'd like to put some magic into the source code that causes a
linker error. Alternatively, I guess I could have DLL initialization
code that somehow detects when more than one instance of the DLL have
been loaded.

TIA - Bob

Another way is to specifically pull in the required library yourself:

#ifdef _DEBUG
#pragma comment(lib, "xerces-lb_2D.lib")
#pragma comment(lib, "xalan-lb_1D.lib")
#else
#pragma comment(lib, "xerces-lb_2.lib")
#pragma comment(lib, "xalan-lb_1.lib")
#endif // _DEBUG

(Obviously, this code was making use of the xerces and xalan libraries)
All you need to do is make sure the path is set to find the .libs.

I've made use of other 3rdParty libraries that do the same thing -
usually in a header file. (Like RogueWave's Stingray)

Dave Connet
 
B

Bob Altman

My original problem description is less than crystal clear. Let me take
another run at it...

I am struggling with a problem similar to what happens if an app links with
the "debug" version of the C runtime library but a library that the app
links with uses the "release" version of the C runtime library. In the case
of the C runtime library, you get a linker error (or maybe it's a warning)
if the main app and all of the libraries that it links with do not all agree
on the C runtime library version (release, debug, DLL, or local).

In my case, I have several native libraries that I'll call A, B, and C. A
calls into B and C, and B calls into C. When I build these libraries in the
"release" configuration, I create A.dll (and A.lib), B.dll, and C.dll.
Also, in the "release" configuration, A specifies B.lib and C.lib as
required input libraries, and B specifies C.lib as a required input library.

When I build these libraries in the "Debug" configuration, I create
A_debug.dll (and A_debug.lib), B_debug.dll, and C_debug.dll. Also, in the
"debug" configuration, A specifies B_debug.lib and C_debug.lib as required
inputs, and B specifies C_debug.lib as a required input. The upshot of all
of this is that I have two different sets of internally consistent
libraries, one compiled for release and one compiled for debug.

Now, the applications that call into these libraries *must* build entirely
with one set of the libraries or the other. That is, an application must
link with A.lib, B.lib, and C.lib *or* A_debug.lib, B_debug.lib, and
C_debug.lib, but *never* with A.lib and B_debug.lib.

So, I'm looking for a way to implement some sort of check to ensure that
code that calls these libraries follows this rule. I would prefer a
link-time check (similar to what happens when the main app and its libraries
don't agree on the C runtime library). But, if I can't do that, I'd settle
for a run-time check. At run-time, if the app calls A.lib then B.lib and
C.lib will be loaded (because A calls into B and C). If the app directly
calls into B_debug.lib then both B.lib and B_debug.lib will be loaded (as
will C.lib and C_debug.lib). I could presumably have some sort of code in
the DLL startup code for B and C that detect this condition.

Huh... I don't know if this is any clearer, but it's definitely longer.
Maybe more is better ;-)

TIA - Bob
 
B

Bob Altman

David Connet said:
Another way is to specifically pull in the required library yourself:

#ifdef _DEBUG
#pragma comment(lib, "xerces-lb_2D.lib")
#pragma comment(lib, "xalan-lb_1D.lib")
#else
#pragma comment(lib, "xerces-lb_2.lib")
#pragma comment(lib, "xalan-lb_1.lib")
#endif // _DEBUG

Thanks Dave. Unfortunately, that doesn't really help my situation. (Take a
look at my second posting. Hopefully it's a little bit clearer than my
original posting.) If a caller explicitly links with the debug version of
one library and the release version of another library then things don't
work correctly. Adding more library references (albeit correct ones)
programmatically doesn't help things, since the linker may still wind up
linking with the incorrect library.
 
D

David Lowndes

So, how can I detect when a program references the debug version of one of
my libraries and the release version of another library? Ideally, I'd like
to put some magic into the source code that causes a linker error.

Bob,

Is (re)naming the library interfaces differently between debug and
release builds a possibility (similar to how Windows defines Ansi &
Unicode APIs)?

Dave
 
B

Bob Altman

David Lowndes said:
Bob,

Is (re)naming the library interfaces differently between debug and
release builds a possibility (similar to how Windows defines Ansi &
Unicode APIs)?

Dave

Hmmm... That's an interesting thought... but, no, I don't think that solves
anything. Here's the problem I have: Suppose the app calls the Initialize
routine in B_debug.dll. That routine does some work and leaves some state
information behind in variables that are private to the dll. Then, the app
incorrectly calls a routine in A.dll (rather than A_debug.dll, which would
have been correct). That routine calls into B (not B_debug) and discovers
that the state variables are not initialized. This is because B and
B_debug are different libraries, each with its own set of state data.
Having two sets of entry point names doesn't change anything in this
example.

Bob
 
D

David Lowndes

Is (re)naming the library interfaces differently between debug and
Hmmm... That's an interesting thought... but, no, I don't think that solves
anything. Here's the problem I have: Suppose the app calls the Initialize
routine in B_debug.dll. That routine does some work and leaves some state
information behind in variables that are private to the dll. Then, the app
incorrectly calls a routine in A.dll (rather than A_debug.dll, which would
have been correct).

Bob,

How (if the functions are named differently between debug & release
libs) would the same code base call into the debug version of one
library and the release version of another - providing the source code
plays by a consistent naming convention/macros you'd use to do the
renaming?

While you can cater for anarchy you can't cater for absolute anarchy
:)

Dave
 
D

David Connet

Thanks Dave. Unfortunately, that doesn't really help my situation.
(Take a look at my second posting. Hopefully it's a little bit
clearer than my original posting.) If a caller explicitly links with
the debug version of one library and the release version of another
library then things don't work correctly. Adding more library
references (albeit correct ones) programmatically doesn't help things,
since the linker may still wind up linking with the incorrect library.

I guess I view this as a developer problem. If you're explicitly linking
to debug and release versions, then all bets are off and you deserve to
crash (yes, it's possible - but you must know what you're doing and the
interface must be designed to support this)! There's only so much you can
do without going crazy trying to prevent stupidity. You can always
program to prevent cluelessness, but stupid people are too clever
(there's a really good quote about this, but I can't remember...)

Dave
 
B

Bob Altman

There's only so much you can
do without going crazy trying to prevent stupidity. You can always
program to prevent cluelessness, but stupid people are too clever
(there's a really good quote about this, but I can't remember...)

Welcome to my world. I spent the better part of a day trying to figure out
why one of my libraries, when called by a coworker's app, seemed to be
broken. Turned out that he had mixed references to the debug and release
versions of the libraries. I got some small satisfaction from slapping him
upside the head when I figured out what was going on, but I'd rather teach
the computer to automagically slap him for me...
 
B

Bob Altman

How (if the functions are named differently between debug & release
libs) would the same code base call into the debug version of one
library and the release version of another - providing the source code
plays by a consistent naming convention/macros you'd use to do the
renaming?


Yes, you are correct. If I could somehow make the names exported by my DLLs
include the postfix "_debug" or "_release" depending on whether I built the
library in the "debug" or "release" configuration, and if I provided macros
in my library's header files to perform a similar transformation based on
whether the caller is building in "debug" or "release", then, yes, the
linker will catch the mismatched library references. If the caller,
building in "debug" configuration, calls A_foo and B_bar (in libraries A and
B respectively), that would turn into actual calls to A_foo_debug and
B_bar_debug. If the caller screwed up and tried to link with the "release"
version of library B then B_bar_debug would not be resolved by the linker.

The downside of this scheme is that changing routine names in the library is
(obviously) a breaking change. I can avoid breaking production code by
appending "_debug" in the debug version of the libraries while keeping the
original routine names in the release version of the libraries.

I think this could work. Now, how do I conditionally append "_debug" to my
routine names?
 
D

David Lowndes

I think this could work. Now, how do I conditionally append "_debug" to my
routine names?

I guess you could have something like this:

#ifdef _DEBUG
#define Fn1 Fn1_debug
#endif

extern void Fn1();

void Fn1()
{
printf( __FUNCTION__ );
}

Dave
 
B

Ben Voigt [C++ MVP]

Bob said:
Welcome to my world. I spent the better part of a day trying to
figure out why one of my libraries, when called by a coworker's app,
seemed to be broken. Turned out that he had mixed references to the
debug and release versions of the libraries. I got some small
satisfaction from slapping him upside the head when I figured out
what was going on, but I'd rather teach the computer to automagically
slap him for me...

It does. Problem is that the error message suggests a way to ignore
multiple definitions instead of fixing the problem.
 
B

Bob Altman

#ifdef _DEBUG
#define Fn1 Fn1_debug
#endif

void Fn1()
{
}

Oh... I didn't realize that I could use a macro like that. That suggests a
way that I can reasonably easily convert all of my exported functions for
the debug build only. I can define a helper macro that evaluates to either
the desired function name or the desired function name plus "_debug", like
this:

In MyLibHelper.h

#ifdef _DEBUG
#define EXPORT(name) name##_debug
#else
#define EXPORT(name) name
#endif

Then I can modify my existing header file and implementation files like
this:

In MyLib.h

#include "MyLibHelper.h"

int EXPORT(MyFunc1)(int arg);
void EXPORT(MyFunc2)(void);
int EXPORT(MyFunc3)(const char* arg1, int arg2);

In the implementation (cpp) files, I would use the same syntax:

#include "MyLibHelper.h"

extern "C" MYLIB_API int EXPORT(MyFunc1)(int arg)
{
<implementation>
}

Thanks a million Dave!

Bob
 
B

Ben Voigt [C++ MVP]

Bob said:
Oh... I didn't realize that I could use a macro like that. That
suggests a way that I can reasonably easily convert all of my
exported functions for the debug build only. I can define a helper
macro that evaluates to either the desired function name or the
desired function name plus "_debug", like this:

In MyLibHelper.h

#ifdef _DEBUG
#define EXPORT(name) name##_debug
#else
#define EXPORT(name) name
#endif

Then I can modify my existing header file and implementation files
like this:

In MyLib.h

#include "MyLibHelper.h"

int EXPORT(MyFunc1)(int arg);
void EXPORT(MyFunc2)(void);
int EXPORT(MyFunc3)(const char* arg1, int arg2);

In the implementation (cpp) files, I would use the same syntax:

#include "MyLibHelper.h"

extern "C" MYLIB_API int EXPORT(MyFunc1)(int arg)
{
<implementation>
}

Trouble is, your users also have to use the same syntax to get the _debug
automatically added. Maybe go with the original idea of redefining the
names themselves. Or better yet, avoid macros (unscoped) and declare a
bunch of inline functions to get the same effect.
 

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

Top