From de57fe2fc9193113dbdf4d53233d47147769173d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6=20Fahlke?= <jorrit@jorrit.de>
Date: Sat, 8 Nov 2014 12:59:38 +0100
Subject: [PATCH] [parametertree][locale][fs1528] Check ParameterTree::get()
 with a locale using "," as the decimal seperator.

ParameterTrees are usually read from configuration files.  The format of the
configuration file should not depend on the locale.

For the commandline parser it would likewise be surprising if "progname -param
0.5" stopped working in a german locale.

Not long ago these kinds of errors would not even generate a diagnosis due to
FS#1527.  However, the locale of the program is "C" by default, so probably
not many poeple are actually affected by this problem.  It can happen however
that other libraries linked into the same program set the locale from the
environment.
---
 dune/common/test/.gitignore                 |  1 +
 dune/common/test/CMakeLists.txt             |  4 +
 dune/common/test/Makefile.am                |  2 +
 dune/common/test/parametertreelocaletest.cc | 97 +++++++++++++++++++++
 4 files changed, 104 insertions(+)
 create mode 100644 dune/common/test/parametertreelocaletest.cc

diff --git a/dune/common/test/.gitignore b/dune/common/test/.gitignore
index c88d294bb..590b258e0 100644
--- a/dune/common/test/.gitignore
+++ b/dune/common/test/.gitignore
@@ -31,6 +31,7 @@
 /mpihelpertest
 /mpihelpertest2
 /nullptr-test
+/parametertreelocaletest
 /parametertreetest
 /pathtest
 /poolallocatortest
diff --git a/dune/common/test/CMakeLists.txt b/dune/common/test/CMakeLists.txt
index a1120ebc3..cc90aab94 100644
--- a/dune/common/test/CMakeLists.txt
+++ b/dune/common/test/CMakeLists.txt
@@ -24,6 +24,7 @@ set(TESTS
     mpihelpertest2
     nullptr_test
     pathtest
+    parametertreelocaletest
     parametertreetest
     poolallocatortest
     shared_ptrtest_config
@@ -126,6 +127,9 @@ add_executable("nullptr_test_fail" EXCLUDE_FROM_ALL nullptr-test.cc)
 target_link_libraries(nullptr_test_fail "dunecommon")
 set_target_properties(nullptr_test_fail PROPERTIES COMPILE_FLAGS "-DFAIL")
 
+add_executable("parametertreelocaletest" parametertreelocaletest.cc)
+target_link_libraries("parametertreelocaletest" "dunecommon")
+
 add_executable("parametertreetest" parametertreetest.cc)
 target_link_libraries("parametertreetest" "dunecommon")
 
diff --git a/dune/common/test/Makefile.am b/dune/common/test/Makefile.am
index fe06436d7..72fea70ad 100644
--- a/dune/common/test/Makefile.am
+++ b/dune/common/test/Makefile.am
@@ -25,6 +25,7 @@ TESTPROGS = \
     mpihelpertest2 \
     nullptr-test \
     pathtest \
+    parametertreelocaletest \
     parametertreetest \
     poolallocatortest \
     shared_ptrtest_config \
@@ -155,6 +156,7 @@ nullptr_test_SOURCES = nullptr-test.cc nullptr-test2.cc
 nullptr_test_fail_SOURCES = nullptr-test.cc
 nullptr_test_fail_CPPFLAGS = $(AM_CPPFLAGS) -DFAIL
 
+parametertreelocaletest_SOURCES = parametertreelocaletest.cc
 parametertreetest_SOURCES = parametertreetest.cc
 
 pathtest_SOURCES = pathtest.cc
diff --git a/dune/common/test/parametertreelocaletest.cc b/dune/common/test/parametertreelocaletest.cc
new file mode 100644
index 000000000..2a2dc2a31
--- /dev/null
+++ b/dune/common/test/parametertreelocaletest.cc
@@ -0,0 +1,97 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <cstdlib>
+#include <iostream>
+#include <locale>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+
+#include <dune/common/exceptions.hh>
+#include <dune/common/parametertree.hh>
+
+// This assert macro does not depend on the value of NDEBUG
+#define check_assert(expr)                                          \
+  do                                                                \
+  {                                                                 \
+    if(!(expr))                                                     \
+    {                                                               \
+      std::cerr << __FILE__ << ":" << __LINE__ << ": check_assert(" \
+                << #expr << ") failed" << std::endl;                \
+      std::abort();                                                 \
+    }                                                               \
+  } while(false)
+
+// Check that the given expression throws the given exception
+#define check_throw(expr, except)                               \
+  do {                                                          \
+    try {                                                       \
+      expr;                                                     \
+      std::cerr << __FILE__ << ":" << __LINE__ << ": " << #expr \
+                << " should throw " << #except << std::endl;    \
+      std::abort();                                             \
+    }                                                           \
+    catch(except) {}                                            \
+    catch(...) {                                                \
+      std::cerr << __FILE__ << ":" << __LINE__ << ": " << #expr \
+                << " should throw " << #except << std::endl;    \
+      std::abort();                                             \
+    }                                                           \
+  } while(false)
+
+// globally set a locale that uses "," as the decimal seperator.
+// return false if no such locale is installed on the system
+bool setCommaLocale()
+{
+  static char const* const commaLocales[] = {
+    "de", "de@euro", "de.UTF-8",
+    "de_AT", "de_AT@euro", "de_AT.UTF-8",
+    "de_BE", "de_BE@euro", "de_BE.UTF-8",
+    "de_CH", "de_CH@euro", "de_CH.UTF-8",
+    "de_DE", "de_DE@euro", "de_DE.UTF-8",
+    "de_LI", "de_LI@euro", "de_LI.UTF-8",
+    "de_LU", "de_LU@euro", "de_LU.UTF-8",
+    NULL
+  };
+  for(char const* const* loc = commaLocales; *loc; ++loc)
+  {
+    try {
+      std::locale::global(std::locale(*loc));
+      std::cout << "Using comma-locale " << std::locale().name() << std::endl;
+      return true;
+    }
+    catch(std::runtime_error) { }
+  }
+
+  std::cout << "No comma-using locale found on system, tried the following:";
+  std::string sep = " ";
+  for(char const* const* loc = commaLocales; *loc; ++loc)
+  {
+    std::cout << sep << *loc;
+    sep = ", ";
+  }
+  std::cout << std::endl;
+  return false;
+}
+
+int main()
+{
+  if(!setCommaLocale())
+  {
+    std::cerr << "No locale using comma as decimal seperator found on system"
+              << std::endl;
+    return 77;
+  }
+  { // Try with comma
+    Dune::ParameterTree ptree;
+    check_throw(ptree["setting"] = "42,42"; ptree.get<double>("setting"),
+                Dune::RangeError);
+  }
+  { // Try with point
+    Dune::ParameterTree ptree;
+    check_assert((ptree["setting"] = "42.42",
+                  ptree.get<double>("setting") == 42.42));
+  }
+}
-- 
GitLab