My application is distributed with two binaries. One which is linked to an old glibc (for example, 2.17) and another binary that targets a newer glibc (for example, 2.34). I also distribute a launcher binary (linked against glibc 2.17) which interrogates the user's libc version and then runs the correct binary.
My code to establish the user's libc looks something like the following:
std::string gnu(gnu_get_libc_version());
double g_ver = std::stod(gnu);
if(g_ver >= 2.34)
{
return GLIBC_MAIN;
}
else
{
return GLIBC_COMPAT;
}
This works perfectly for the best part, however, some of my users report that despite having a new glibc, the old glibc binary is actually run. I have investigated this and have discovered that double g_ver is equal to 2, not 2.34 as it should be. That is, the decimal part is missing. gnu_get_libc_version() always has the correct value so it must be a problem when converting the string to a double.
I have also tried boost::lexical_cast but this has the same effect.
std::string gnu(gnu_get_libc_version());
//double g_ver = std::stod(gnu);
double glibc = boost::lexical_cast<double>(gnu);
if(g_ver >= 2.34)
{
return GLIBC_MAIN;
}
else
{
return GLIBC_COMPAT;
}
Needless to say, I am unable to reproduce this behaviour on any of my computers even when running the exact same distribution / version of Linux as affected users.
Does anybody know why boost::lexical_cast or std::stod sometimes misses the decimal part of the version number? Is there an alternative approach to this?
UPDATE
Upon further tests, this problem is introduced when using a different locale. I set my locale to fr_FR.UTF-8 on a test machine and was able to reproduce this problem. However, the output of gnu_get_libc_version() seems to be correct but std::stod is unable to parse the decimal point part of the version.
Does anybody know how to correct this problem?
The fundamental problem is that the glibc version is a string and not a decimal number. So for a "proper" solution you need to parse it manually and implement your own logic to decide which version is bigger or smaller.
However, as a quick and dirty hack, try inserting the line
setlocale(LC_NUMERIC, "C");
before the strtod
call. That will set the numeric locale back to the default C locale where the decimal separator is .
. If you're doing something that needs correct locales later in the program you need to set it back again. Depending on how your program initialized locales earlier, something like
setlocale(LC_NUMERIC, "");
should reset it back to what the environment says the locale should be.