Library / header compilation mismatch

S

stephen.diverdi

This isn't strictly a language issue, but it seems enough of a cross-
IDE and -platform concern to merit asking here.

I'm running into a maintenance issue for a library I've written. The
problem is I have a config.h file that includes a bunch of #define
directives that control the compilation of the library. Those
directives can also be overridden by options to the compiler. The
problem arises when I compile the library with one set of directives,
and then in a project that links to the library, compiler options
override the library's directives and the headers that are included
don't match the library that was compiled. Strange errors and fun
debugging ensues.

I'd like to find a way to test for this condition automatically,
ideally at compile time without runtime overhead. One option is to
provide something like:

config.h:
#define FLAG_SSE 0x01
#define FLAG_OPENGL 0x02
#define FLAG_WIN32 0x04
bool checkCompilationFlags ( int flags );

config.cpp:
static int COMPILATION_FLAGS = FLAG_SSE | FLAG_OPENGL | FLAG_WIN32;
bool checkCompilationFlags ( int flags ) { return ( flags ==
COMPILATION_FLAGS ); }

Therefore, when the library is compiled, COMPILATION_FLAGS will store
the configuration that was used, and the application can call
checkCompilationFlags with its understood values for the flags and see
if they match. This isn't great though, as it requires calling the
function somewhere in the application, it adds some (small) runtime
overhead, and it only raises the error at runtime. A perfect solution
would not require anything more than including the headers in the
application, would compile away with optimization, and would raise a
compile-time error in the event of a mismatch.

Has anyone dealt with this issue before? Are there any standard
approaches or clever tricks? Any ideas? Thanks,

-stephen diverdi
(e-mail address removed)
 
S

stephen.diverdi

I am sure folks who release[d] a library for more than one platform
have. And I am *more than certain* that there is *no* "standard
approach". As for "clever tricks", cleverness is in the eye of the
beholder, but I would bet that if solutions exist, they are *all*
compiler-specific. Consider asking in the corresponding compiler
newsgroups.

The example solution that I posted is not compiler-specific. I can
further see a straightforward way to extend it to not require
explicitly calling a function on the application's part, with a static
object whose constructor executes the test. This is still runtime
overhead and a runtime error however. In my experience, there are
often solutions to problems of this nature in the form of template
metaprogramming "clever tricks", and in fact they are often
implemented in things like boost's MPL - for example, I've used
boost's compile time assertion, which I would describe as a "standard
approach" (more than rolling my own anyway). I'm simply asking if
anyone more knowledgeable than I has any good ideas or relevant
experience. Thanks,

-stephen diverdi
(e-mail address removed)
 
S

stephen.diverdi

It wasn't?  I thought it hinged upon compiler-generated header file
(config.h or something), where the compiler *options* would be in
some way converted into macros.  (a) All compiler options are not
the same (b) not all compilers have the ability to convert command-
line options into macros (do any of them have that?).  So, you're
clearly relying on a compiler that is capable of converting some
specific options (common among a limited set of compilers, perhaps)
into a macro and storing it in a file to be included for further
processing by the same compiler.  What did I miss?
I'm sorry, I guess I wasn't clear. My idea wasn't that it would
capture the state of all possible compiler options, but that it would
address only those flags that the user has created in the config.hpp
file. E.g. for my library, there's FLAG_SSE and FLAG_OPENGL in
config.hpp, which are used to conditionally compile blocks of code in
the library. I was only considering something that would capture the
state of (in this limited example) those two flags. Even though
they're in the config.hpp file, they're written as such:

config.hpp:
#ifndef FLAG_SSE
#define FLAG_SSE 0
#endif

This way, the library has defaults, but they can be overridden w/o
modifying the files via compiler options like -DFLAG_SSE=1. The
solution that I outlined in my original post would be able to
correctly capture the state of the set of flags whether they were set
from config.hpp or from the command line.

You're right though, there is a larger issue of compiler-specific
compilation options that have an impact as well. If those options set
the values of preprocessor macros (e.g. compiling with SSE turned on
often sets something like __SSE__=1), then the mechanism could also be
made to handle them (requiring some coding for each flag to be
supported, though such code could be reused across projects). That's
definitely larger than the original scope I was considering.

Thanks,

-stephen diverdi
(e-mail address removed)
 
J

James Kanze

This isn't strictly a language issue, but it seems enough of a
cross- IDE and -platform concern to merit asking here.
I'm running into a maintenance issue for a library I've
written. The problem is I have a config.h file that includes
a bunch of #define directives that control the compilation of
the library. Those directives can also be overridden by
options to the compiler. The problem arises when I compile
the library with one set of directives, and then in a project
that links to the library, compiler options override the
library's directives and the headers that are included don't
match the library that was compiled. Strange errors and fun
debugging ensues.
I'd like to find a way to test for this condition
automatically, ideally at compile time without runtime
overhead. One option is to provide something like:
config.h:
#define FLAG_SSE 0x01
#define FLAG_OPENGL 0x02
#define FLAG_WIN32 0x04
bool checkCompilationFlags ( int flags );
config.cpp:
static int COMPILATION_FLAGS = FLAG_SSE | FLAG_OPENGL | FLAG_WIN32;
bool checkCompilationFlags ( int flags ) { return ( flags ==
COMPILATION_FLAGS ); }
Therefore, when the library is compiled, COMPILATION_FLAGS
will store the configuration that was used, and the
application can call checkCompilationFlags with its understood
values for the flags and see if they match. This isn't great
though, as it requires calling the function somewhere in the
application, it adds some (small) runtime overhead, and it
only raises the error at runtime. A perfect solution would
not require anything more than including the headers in the
application, would compile away with optimization, and would
raise a compile-time error in the event of a mismatch.
Has anyone dealt with this issue before? Are there any
standard approaches or clever tricks? Any ideas? Thanks,

I don't do it for options (yet---it's a good idea for options
which affect binary compatibility), but I encode the version in
the namespace name. Something like:

#define PASTE(a,b) a ## b
#define MyNamespace PASTE( MyNamespace_, versionId )
namespace MyNamespace {
// ...
}

If you link against the wrong version, you get all of your
symbols undefined.

As long as the flags you're interested in are hexadecimal
constants, written as above, you could do the same thing---all
of the characters in a hexadecimal literal are legal in an
indentifier.

The only downside might be if clients want to use a debugger;
the debugger isn't going to find the symbol MyNamespace::toto.
 
S

stephen.diverdi

I don't do it for options (yet---it's a good idea for options
which affect binary compatibility), but I encode the version in
the namespace name.  Something like:

    #define PASTE(a,b) a ## b
    #define MyNamespace PASTE( MyNamespace_, versionId )
    namespace MyNamespace {
    // ...
    }

If you link against the wrong version, you get all of your
symbols undefined.

Ah, that's great, thank you! A namespace is a perfect solution to the
problem.
The only downside might be if clients want to use a debugger;
the debugger isn't going to find the symbol MyNamespace::toto.

Right, well, I could just make a small namespace with a dummy
something or other in it for the purposes of version/option checking,
while keeping the rest of the code in MyNamespace.

Thank you!

-stephen diverdi
(e-mail address removed)
 
R

red floyd

Ah, that's great, thank you!  A namespace is a perfect solution to the
problem.

Use an alias.

#define PASTE(a,b) a ## b
#define MyNamespace_ PASTE( MyNamespace_, versionId )
namespace MyNamespace_ {
// ...
}
namespace MyNamespace = MyNamespace_;
 
J

James Kanze

Use an alias.
#define PASTE(a,b) a ## b
#define MyNamespace_ PASTE( MyNamespace_, versionId )
namespace MyNamespace_ {
// ...
}
namespace MyNamespace = MyNamespace_;

That's what I actually did---I wanted the user to only know a
single namespace name. In practice, however, you can't use the
alias in a namespace definition, and the debugger (at least g++)
doesn't know about it, so it really doesn't buy you much. I'm
still using it, because that's the way I started (and at least
with some compilers, like g++, the alias is what appears in
error messages---if you mispell a member, for example, g++ will
output "'xxx' is not a member of 'Alias'").

For what it's worth, gdb will do automatic completion, so if you
have something like:

#define VersionedMine PASTE(Mine_,version)
namespace VersionedMine {}
namespace Mine = VersionedMine ;

in a common header, you still have to define things in

namespace VersionedMine {
// ...
}

but client code can use Mine, and in gdb, if they enter
Mine<TAB>, gdb will complete it with whatever is appropriate.
It's not perfect, but it's a fairly usable.

BTW: I'm not the inventor of this idea. I think I got it from
Nathan Meyrs, but I'm not sure. But it's been around almost
since namespaces were introduced into the language.
 

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,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top