Skip to content

Use locale independent conversion from double to string#1099

Merged
davetcoleman merged 13 commits intomoveit:kinetic-develfrom
isys-vision:fix-locale-ompl-interface
Oct 26, 2018
Merged

Use locale independent conversion from double to string#1099
davetcoleman merged 13 commits intomoveit:kinetic-develfrom
isys-vision:fix-locale-ompl-interface

Conversation

@simonschmeisser
Copy link
Copy Markdown
Contributor

boost::lexical_caststd::string(0.1) returns "0,1" on Dutch, German and probably some other locales
which boost::lexical_cast fails to parse even on Dutch, German etc locales

so this PR uses std::ostringstream which seems to be the only sane way to ignore locale

(this is no new problem, it somehow seemed to "sometimes" work to put strings (ie '0.1') instead off the raw numbers (ie 0.1) in ompl_planning.yaml but not always ...)

boost::lexical_cast<std::string>(0.1) returns "0,1" on Dutch, German and probably some other locales
which boost::lexical_cast<double> fails to parse even on Dutch, German etc locales

so this commit uses std::ostringstream which seems to be the only sane way to ignore locale
Copy link
Copy Markdown
Contributor

@rhaschke rhaschke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree to the need of this fix. However, instead of repeating the same code snippet over and over, we should aim for a generic solution at a central place. We already have macros and exceptions. However, both folders have only a single header / source file. I suggest a more generic utils folder:

  • utils/CMakeLists.txt (building libmoveit_utils)
  • utils/include/moveit/utils/conversions.h
  • utils/src/conversions.cpp

There we could implement a moveit::lexical_cast.

{
// convert to string using no locale
std::ostringstream oss;
oss.imbue(std::locale());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that's really neccessary at all.
If so, I guess you need to use std::locale::classic()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, using std::locale::classic() is indeed better. Omitting it would probably/usually/maybe also be right unless somebody somewhere set the global locale to something else.

As a side note, using std::stod for the inverse operation does not work as this uses the c locale internally which does seem to get set automatically from the system.

std::locale() copies the global locale, which is already used at construction time of std::stringstream. So this was a noop.
The global locale defaults to std::locale::classic() but it might have been set somewhere else before,
 so explicitly setting std::locale::classic() makes this code independent of locale settings elsewhere.
@rhaschke
Copy link
Copy Markdown
Contributor

@simonschmeisser Please factor out this conversion code into its own library.

@simonschmeisser
Copy link
Copy Markdown
Contributor Author

simonschmeisser commented Oct 19, 2018

sure, @rhaschke , one thing at its time ;) would you prefer

  • a full featured moveit_tools::lexical_cast (that uses template specialization for double/float and boost::lexical_cast otherwise)
  • or just a simple moveit_tools::toStringLocaleIndependent(float/double) and use std::to_string otherwise? (would require a moveit_tools::toString(bool) as well)

@simonschmeisser
Copy link
Copy Markdown
Contributor Author

github sometimes forgets about my further commits, I'm confused

@gavanderhoorn
Copy link
Copy Markdown
Member

github sometimes forgets about my further commits, I'm confused

https://status.github.com/messages

Copy link
Copy Markdown
Contributor

@rhaschke rhaschke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adapting this. Some minor comments inline.

  • Did you checked for all occurences of boost::lexical_cast in the source? Are there any other locations that need an updated?
  • What about conversion fromString to a value? Shouldn't we have appropriate C-locale functions for that too?

{
/** \brief Convert a double to std::string without using the system locale

Depending on the system locale, a different decimal seperator might be used
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to move this comment to the top as a general comment for this file.

{
namespace utils
{
std::string toString(double d)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation using function overloading is somewhat redundant.
Why didn't you choose templates as discussed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are only two variable types (float and double) where these functions are required. For other variable types (integer types) std::to_string would be a more obvious solution? This can of course be handled using template specialization if requested but imho just adds compile time and unneeded abstractions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonschmeisser I always try to prevent code duplication as much as possible - even in this simple case. In this particular case, I would suggest to use a template defined in .cpp only, providing the common implementation for both overloaded functions. This doesn't add compile time or any other complexity to the API.

Please, use github's Reply button to answer such remarks. This way, the thread is kept together.

Co-Authored-By: simonschmeisser <s.schmeisser@gmx.net>
@simonschmeisser
Copy link
Copy Markdown
Contributor Author

about string->float/double: from a bug fixing point of view it's not necessary since boost::lexical_cast(string) expects "." independent of locale (there is not a single line about locales in the documentation of boost::lexical_cast so ...)

from a maintenance point of view (as boost::lexical_cast people do not seem to know what a locale is ...) it might be better to indeed add this inverse function

@simonschmeisser
Copy link
Copy Markdown
Contributor Author

There is only one more places where the conversion double -> string is done incorrectly:
https://github.com/ros-planning/moveit/blob/5a1d5a8f172c43aedee9f47926068e3ac529d312/moveit_ros/benchmarks/benchmarks/src/benchmark_execution.cpp#L812

should I include it in this PR?

@rhaschke
Copy link
Copy Markdown
Contributor

There is only one more place where the conversion double -> string is done incorrectly. Should I include it in this PR?

Yes, please.

now throws a std::runtime_exception if errors occur.
For planners this gets caught in planning_pipeline::PlanningPipeline::generatePlan
and there are no further users of toDouble/toFloat currently
Copy link
Copy Markdown
Contributor

@rhaschke rhaschke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for those fixes. One more nit-pick.

@rhaschke rhaschke added the awaits 2nd review one maintainer approved this request label Oct 24, 2018
moveit_distance_field
moveit_kinematics_metrics
moveit_dynamics_solver
moveit_utils
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a package, remove

Copy link
Copy Markdown
Contributor Author

@simonschmeisser simonschmeisser Oct 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this section is about what libraries are exported by the moveit_core package, you get linker errors in dependent packages if moveit_utils is not added here

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops sorry. the github code folding hid the relevant section and i made a wrong assumption

/* Author: Simon Schmeisser */

#ifndef MOVEIT_UTILS_
#define MOVEIT_UTILS_
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add MOVEIT_CORE_ to front

#include <string>
namespace moveit
{
namespace utils
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should have namespace core also, or instead of utils, as used in all the other subfolders of moveit_core

std::string toString(double d);

/** \brief Convert a float to std::string using the classic C locale */
std::string toString(float d);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::string toString(float d);
std::string toString(float f);

var name makes more sense

// convert from string using no locale
std::istringstream iss(s);
iss.imbue(std::locale::classic());
T d;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are too many one character variable names here, hard to read. add longer variable names

return toStringImpl(d);
}

template <class T>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think T would be clearer if called something like OutputDataType

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nit-picking. T is the standard for a template type. Only if more than one type is involved, there is a need in more verbose type names.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair. just pulling in recent experiences in google-style projects, but ignore my comment

@rhaschke
Copy link
Copy Markdown
Contributor

LGTM

moveit_distance_field
moveit_kinematics_metrics
moveit_dynamics_solver
moveit_utils
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops sorry. the github code folding hid the relevant section and i made a wrong assumption

@davetcoleman davetcoleman merged commit e855e7f into moveit:kinetic-devel Oct 26, 2018
davetcoleman pushed a commit to davetcoleman/moveit that referenced this pull request Oct 26, 2018
* Use locale independent conversion from double to string

boost::lexical_cast<std::string>(0.1) returns "0,1" on Dutch, German and probably some other locales
which boost::lexical_cast<double> fails to parse even on Dutch, German etc locales

so this commit uses std::ostringstream which seems to be the only sane way to ignore locale

* clang format

* use std::locale::classic() instead of std::locale()

std::locale() copies the global locale, which is already used at construction time of std::stringstream. So this was a noop.
The global locale defaults to std::locale::classic() but it might have been set somewhere else before,
 so explicitly setting std::locale::classic() makes this code independent of locale settings elsewhere.

* factor out conversion from double/float to string into moveit_utils

* clang format

* more explicit description

Co-Authored-By: simonschmeisser <s.schmeisser@gmx.net>

* adress documentation comments

* add and use conversion functions from string to double

* Don't write localized doubles to benchmark results

* use template internally to avoid code duplication

* Add error handling for std::string->float/double

now throws a std::runtime_exception if errors occur.
For planners this gets caught in planning_pipeline::PlanningPipeline::generatePlan
and there are no further users of toDouble/toFloat currently

* remove extra parantheses again

* Code review comments by Dave
@davetcoleman
Copy link
Copy Markdown
Member

Melodic: #1155

rhaschke pushed a commit to ubi-agni/moveit that referenced this pull request Oct 26, 2018
* Use locale independent conversion from double to string

boost::lexical_cast<std::string>(0.1) returns "0,1" on Dutch, German and probably some other locales
which boost::lexical_cast<double> fails to parse even on Dutch, German etc locales

so this commit uses std::ostringstream which seems to be the only sane way to ignore locale

* clang format

* use std::locale::classic() instead of std::locale()

std::locale() copies the global locale, which is already used at construction time of std::stringstream. So this was a noop.
The global locale defaults to std::locale::classic() but it might have been set somewhere else before,
 so explicitly setting std::locale::classic() makes this code independent of locale settings elsewhere.

* factor out conversion from double/float to string into moveit_utils

* clang format

* more explicit description

Co-Authored-By: simonschmeisser <s.schmeisser@gmx.net>

* adress documentation comments

* add and use conversion functions from string to double

* Don't write localized doubles to benchmark results

* use template internally to avoid code duplication

* Add error handling for std::string->float/double

now throws a std::runtime_exception if errors occur.
For planners this gets caught in planning_pipeline::PlanningPipeline::generatePlan
and there are no further users of toDouble/toFloat currently

* remove extra parantheses again

* Code review comments by Dave
rhaschke pushed a commit to ubi-agni/moveit that referenced this pull request Oct 26, 2018
pull bot pushed a commit to shadow-robot/moveit that referenced this pull request Sep 3, 2020
* Use locale independent conversion from double to string

boost::lexical_cast<std::string>(0.1) returns "0,1" on Dutch, German and probably some other locales
which boost::lexical_cast<double> fails to parse even on Dutch, German etc locales

so this commit uses std::ostringstream which seems to be the only sane way to ignore locale

* clang format

* use std::locale::classic() instead of std::locale()

std::locale() copies the global locale, which is already used at construction time of std::stringstream. So this was a noop.
The global locale defaults to std::locale::classic() but it might have been set somewhere else before,
 so explicitly setting std::locale::classic() makes this code independent of locale settings elsewhere.

* factor out conversion from double/float to string into moveit_utils

* clang format

* more explicit description

Co-Authored-By: simonschmeisser <s.schmeisser@gmx.net>

* adress documentation comments

* add and use conversion functions from string to double

* Don't write localized doubles to benchmark results

* use template internally to avoid code duplication

* Add error handling for std::string->float/double

now throws a std::runtime_exception if errors occur.
For planners this gets caught in planning_pipeline::PlanningPipeline::generatePlan
and there are no further users of toDouble/toFloat currently

* remove extra parantheses again

* Code review comments by Dave
JafarAbdi pushed a commit to JafarAbdi/moveit that referenced this pull request Mar 24, 2022
* Remove OgrePrerequisites include

* octomap_render.h includes itself

* Include OgrePrerequisites.h
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaits 2nd review one maintainer approved this request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants