Supporting multiple incompatible versions of C++ library
Frage
I wrote a Ruby C++ extension to wrap Google's re2 regular expression library but a recent update to the library changed the interface of the Match()
function from:
bool Match(const StringPiece& text,
int startpos,
Anchor anchor,
StringPiece *match,
int nmatch) const;
To:
bool Match(const StringPiece& text,
int startpos,
int endpos,
Anchor anchor,
StringPiece *match,
int nmatch) const;
(Note the new int endpos
argument.)
My question is: is it possible for me to support both versions of this library in one extension despite the fact that re2 does not seem to specify any kind of VERSION
constant that I can introspect?
Ideally, I want to be able to try the newer version (with 6 arguments) and, failing that, fall back to the older version (as I am able to backfill the endpos
argument easily).
At the moment my code is like this:
matched = p->pattern->Match(text_as_string_piece, 0, (int)RSTRING_LEN(text), RE2::UNANCHORED, 0, 0);
But if you have the older version of re2, it needs to be:
matched = p->pattern->Match(text_as_string_piece, 0, RE2::UNANCHORED, 0, 0);
Lösung
The traditional answer is to generate a config.h
or whatever at installation time.
That is, at installation you will detect which version of re2
is installed, and then define a symbol in config.h
depending on this:
// config.h
#ifndef CONFIG_H_INCLUDED
#define CONFIG_H_INCLUDED
#define RE2_MATCH_6_ARGS 1
#endif // CONFIG_H_INCLUDED
And then, you can use this:
#if defined(RE2_MATCH_6_ARGS) && RE2_MATCH_6_ARGS == 1
matched = p->pattern->Match(text_as_string_piece, 0, (int)RSTRING_LEN(text), RE2::UNANCHORED, 0, 0);
#else
matched = p->pattern->Match(text_as_string_piece, 0, RE2::UNANCHORED, 0, 0);
#endif
This is a stable solution, and should work well.
Another possibility is to hack the system...
Provide a definition of both Match
functions in a wrapper library:
Match5
throws an error (please link to re2)Match6
forwards toMatch5
.
The thing is (in the Unix world...), if a symbol has already been loaded, it will not be overwritten by a new definition. So as long as re2
is loaded first you end up in one of those two scenarios:
- old
re2
: your (wrapper) library provides a definition ofMatch6
which forwards to there2
provided definition ofMatch5
- new
re2
: the call directly goes to the definition ofMatch6
thatre2
provided
Much more brittle. Requires a wrapper library around re2
. Unlikely to work with static linking (never tried though...). But does not require a ./configure
step.
Andere Tipps
From what I know, you can't. This would require you to link with both the old and the new versions of RE2, which would make namespaces collide.
Unless there exists a version of this method that ressembles the old version, you're stuck with the new version.
If you want to be able to compile your extension for both versions, then you have to modify your compiling steps to define some flag that you control.
#ifdef RE2_ODLFORMAT
matched = p->pattern->Match(text_as_string_piece, 0, RE2::UNANCHORED, 0, 0);
#else
matched = p->pattern->Match(text_as_string_piece, 0, (int)RSTRING_LEN(text), RE2::UNANCHORED, 0, 0);
#endif
Then to compile your code, you'd do something along those lines:
make RE2_OLDFORMAT=1 all # compile for old version
make all # default target is for new version