Skip to content

Commit

Permalink
V2.3.x (#107)
Browse files Browse the repository at this point in the history
* added compound assignment operators: +=, -=, *=, /=. Closes #44.

* print undefined unit types in SI base units. Closes #38. Update constants to NIST 2014 CODATA recommendations.

* Reduce metaprogramming boilerplate

* workaround for vs2013 internal compiler error

* added data and data transfer units. Closes #46.

* update doxyfile.in to v2.2.0

* add constexpr std::array test

* add julian and gregorian year definitions. Closes #29.

* added `value()` and `value_type` trait. Closes #56.

* fixes constexpr relational operator bug. Closes #63.

* improve compile time.

* removed some debug code.

* measure compile time

* Friend the linear scale units. Reduces compile time.

* add inline to pow() and abs() for being able to use units.h in different compilation units

* Revert "Friend the linear scale units. Reduces compile time."

This reverts commit e26fcd7.

* Revert "add inline to pow() and abs() for being able to use units.h in different compilation units"

This reverts commit d42020e.

* add inline to pow() and abs() for being able to use units.h in different compilation units

* Test with Visual Studio 2017

* Another attempt at using Visual Studio 2017 in appveyor

* Build with VS2017

* add MSVC2017 badge

* started min/max

* fixes #65, fixes #71, fixes #76, fixes #79, fixes #80.  Adds documentation.

* fix for vs2013, gcc

* updated docs

* fix for vs2013

* remove gcc warnings, add prefined unit defines

* min/max shouldn't be constexpr

* fix cmake

* more cmake fixes

* numeric_limits namespace

* fixes #77.

* fixes #75 & documentation

* fixed merge issues

* for Morwenn

* Make the project work with both ninja and msvc generators on windows

* Fixes #106. Locale aware `to_string` conversions.

* Fixes #105. Unary operators. Also removes VS 2013 testing (and support)

* Fixes #87. Don't include `to_string()` when iostream is disabled.

* fix a mistake in the unary +/-

* Add constexpr name/abbreviation member functions which don't require iostream.

* Fixes for linux build
  • Loading branch information
nholthaus authored Apr 21, 2018
1 parent 2342332 commit 3252189
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ env:

install:
- sudo apt-get update -qq
- sudo apt-get install -y -qq lcov curl
- sudo apt-get install -y -qq lcov curl language-pack-de
- if [[ "$CXX" = "g++" ]]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
- pwd
- ls
Expand Down
45 changes: 38 additions & 7 deletions 3rdParty/gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,39 @@ STRING(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER)
SET(VERSION_${PROJECT_NAME_UPPER} "?.?.?" CACHE STRING "${PROJECT_NAME} version string")
SET(${PROJECT_NAME_UPPER}_DIR ${CMAKE_CURRENT_BINARY_DIR}/src CACHE STRING "${PROJECT_NAME} source directory")

# ------------------------------------------------------------------------------------------------------------------------------
# LIBRARY LOCATION: Library location for various build types/generators
# ------------------------------------------------------------------------------------------------------------------------------

if((${CMAKE_GENERATOR} STREQUAL "Ninja") OR (${CMAKE_GENERATOR} STREQUAL "Unix Makefiles"))
SET(LIB_DIR ${${PROJECT_NAME_UPPER}_DIR}/googletest/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX})
else() # visual studio variants
SET(DEBUG_LIB ${${PROJECT_NAME_UPPER}_DIR}/googletest/Debug/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES})
SET(RELEASE_LIB ${${PROJECT_NAME_UPPER}_DIR}/googletest/Release/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES})
SET(MINSIZEREL_LIB ${${PROJECT_NAME_UPPER}_DIR}/googletest/MinSizeRel/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES})
SET(RELWITHDEBINFO_LIB ${${PROJECT_NAME_UPPER}_DIR}/googletest/RelWithDebInfo/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES})
SET(LIB_DIR ${DEBUG_LIB} ${RELEASE_LIB} ${MINSIZEREL_LIB} ${RELWITHDEBINFO_LIB})
endif()

# ------------------------------------------------------------------------------------------------------------------------------
# EXTERNAL PROJECT: Definition of the external project to build the third-party library
# ------------------------------------------------------------------------------------------------------------------------------

IF(WIN32)
SET(WINDOWS_CMAKE_ARGS -DCMAKE_CXX_FLAGS=/D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
ENDIF(WIN32)

include(ExternalProject)
ExternalProject_Add(
${PROJECT_NAME}-ext
URL ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}-${VERSION_${PROJECT_NAME_UPPER}}.zip
URL_MD5 adfafc8512ab65fd3cf7955ef0100ff5
DOWNLOAD_DIR ${${PROJECT_NAME_UPPER}_DIR}
SOURCE_DIR ${${PROJECT_NAME_UPPER}_DIR}
CMAKE_ARGS -DBUILD_GMOCK=OFF -DBUILD_GTEST=ON -Dgtest_force_shared_crt=ON -DCMAKE_CXX_FLAGS=/D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING
CMAKE_ARGS -DBUILD_GMOCK=OFF -DBUILD_GTEST=ON -Dgtest_force_shared_crt=ON ${WINDOWS_CMAKE_ARGS}
INSTALL_COMMAND ""
BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS ${LIB_DIR}
)

# ------------------------------------------------------------------------------------------------------------------------------
Expand All @@ -25,19 +48,27 @@ ExternalProject_Add(

# Library
add_library(${PROJECT_NAME} STATIC IMPORTED GLOBAL)
IF(WIN32)
if((${CMAKE_GENERATOR} STREQUAL "Ninja") OR (${CMAKE_GENERATOR} STREQUAL "Unix Makefiles"))
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
IMPORTED_LOCATION_DEBUG ${${PROJECT_NAME_UPPER}_DIR}/googletest/Debug/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES}
IMPORTED_LOCATION_RELEASE ${${PROJECT_NAME_UPPER}_DIR}/googletest/Release/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES}
IMPORTED_LOCATION_MINSIZEREL ${${PROJECT_NAME_UPPER}_DIR}/googletest/MinSizeRel/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES}
IMPORTED_LOCATION_RELWITHDEBINFO ${${PROJECT_NAME_UPPER}_DIR}/googletest/RelWithDebInfo/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_FIND_LIBRARY_SUFFIXES}
IMPORTED_LOCATION ${LIB_DIR}
)
else() # visual studio variants
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
IMPORTED_LOCATION_DEBUG ${DEBUG_LIB}
IMPORTED_LOCATION_RELEASE ${RELEASE_LIB}
IMPORTED_LOCATION_MINSIZEREL ${MINSIZEREL_LIB}
IMPORTED_LOCATION_RELWITHDEBINFO ${RELWITHDEBINFO_LIB}
)
ENDIF()

# add pthreads on non-windows platforms
IF(WIN32)
ELSE(WIN32)
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
IMPORTED_LOCATION ${${PROJECT_NAME_UPPER}_DIR}/googletest/${CMAKE_FIND_LIBRARY_PREFIXES}${PROJECT_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}
INTERFACE_LINK_LIBRARIES -pthread
)
ENDIF(WIN32)

add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}-ext)

SET(INCLUDE_DIR ${${PROJECT_NAME_UPPER}_DIR}/googletest/include)
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ endif(DISABLE_IOSTREAM)
# unit tests
if(BUILD_TESTS)
set(VERSION_GTEST "1.8.0" CACHE STRING "Google Test framework version")
enable_testing ()
enable_testing()
add_subdirectory(3rdParty/gtest)
add_subdirectory(unitTests)
endif(BUILD_TESTS)
Expand Down
27 changes: 27 additions & 0 deletions CMakeSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
// See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file.
"configurations": [
{
"name": "Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
{
"name": "Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}
]
}
1 change: 0 additions & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ configuration:

environment:
matrix:
- VS_GEN: Visual Studio 12 2013 Win64
- VS_GEN: Visual Studio 14 2015 Win64
- VS_GEN: Visual Studio 15 2017 Win64

Expand Down
139 changes: 115 additions & 24 deletions include/units.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,37 @@
#if !defined(UNIT_LIB_DISABLE_IOSTREAM)
#include <iostream>
#include <string>
#endif
#include <locale>

//------------------------------
// STRING FORMATTER
//------------------------------
//------------------------------
// STRING FORMATTER
//------------------------------

namespace units
{
namespace detail
namespace units
{
template <typename T> std::string to_string(const T& t)
namespace detail
{
std::string str{ std::to_string(t) };
int offset{ 1 };
if (str.find_last_not_of('0') == str.find('.')) { offset = 0; }
str.erase(str.find_last_not_of('0') + offset, std::string::npos);
return str;
template <typename T> std::string to_string(const T& t)
{
std::string str{ std::to_string(t) };
int offset{ 1 };

// remove trailing decimal points for integer value units. Locale aware!
struct lconv * lc;
lc = localeconv();
char decimalPoint = *lc->decimal_point;
if (str.find_last_not_of('0') == str.find(decimalPoint)) { offset = 0; }
str.erase(str.find_last_not_of('0') + offset, std::string::npos);
return str;
}
}
}
#endif

namespace units
{
template<typename T> inline constexpr const char* name(const T&);
template<typename T> inline constexpr const char* abbreviation(const T&);
}

//------------------------------
Expand Down Expand Up @@ -142,7 +154,7 @@ namespace units
#define UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\
namespace namespaceName\
{\
/** @name Unit Containers */ /** @{ */ typedef unit_t<nameSingular> nameSingular ## _t; /** @} */\
/** @name Unit Containers */ /** @{ */ typedef unit_t<nameSingular> nameSingular ## _t; /** @} */\
}

/**
Expand Down Expand Up @@ -182,13 +194,29 @@ namespace units
{\
return units::detail::to_string(obj()) + std::string(" "#abbrev);\
}\
inline constexpr const char* abbreviation(const nameSingular ## _t&)\
{\
return #abbrev;\
}\
}
#endif

/**
* @def UNIT_ADD_NAME(namespaceName,nameSingular,abbreviation)
* @brief Macro for generating constexpr names/abbreviations for units.
* @details The macro generates names for units. E.g. name() of 1_m would be "meter", and
* abbreviation would be "m".
* @param namespaceName namespace in which the new units will be encapsulated. All literal values
* are placed in the `units::literals` namespace.
* @param nameSingular singular version of the unit name, e.g. 'meter'
* @param abbreviation - abbreviated unit name, e.g. 'm'
*/
#define UNIT_ADD_NAME(namespaceName, nameSingular, abbrev)\
template<> inline constexpr const char* name(const namespaceName::nameSingular ## _t&)\
{\
return #nameSingular;\
}\
template<> inline constexpr const char* abbreviation(const namespaceName::nameSingular ## _t&)\
{\
return #abbrev;\
}

/**
* @def UNIT_ADD_LITERALS(namespaceName,nameSingular,abbreviation)
* @brief Macro for generating user-defined literals for units.
Expand Down Expand Up @@ -239,6 +267,7 @@ namespace units
#define UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\
UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, __VA_ARGS__)\
UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\
UNIT_ADD_NAME(namespaceName,nameSingular, abbreviation)\
UNIT_ADD_IO(namespaceName,nameSingular, abbreviation)\
UNIT_ADD_LITERALS(namespaceName,nameSingular, abbreviation)

Expand Down Expand Up @@ -2112,6 +2141,22 @@ namespace units
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double, std::nano>(units::convert<Units, unit<std::ratio<1,1000000000>, category::time_unit>>((*this)())));
}

/**
* @brief returns the unit name
*/
inline constexpr const char* name() const noexcept
{
return units::name(*this);
}

/**
* @brief returns the unit abbreviation
*/
inline constexpr const char* abbreviation() const noexcept
{
return units::abbreviation(*this);
}

public:

template<class U, typename Ty, template<typename> class Nlt>
Expand Down Expand Up @@ -2196,12 +2241,6 @@ namespace units
}
#endif

template<class Units, typename T, template<typename> class NonLinearScale>
constexpr unit_t<Units, T, NonLinearScale> operator-(const unit_t<Units, T, NonLinearScale>& val) noexcept
{
return unit_t<Units, T, NonLinearScale>(-val());
}

template<class Units, typename T, template<typename> class NonLinearScale, typename RhsType>
inline unit_t<Units, T, NonLinearScale>& operator+=(unit_t<Units, T, NonLinearScale>& lhs, const RhsType& rhs) noexcept
{
Expand Down Expand Up @@ -2244,6 +2283,58 @@ namespace units
return lhs;
}

//------------------------------
// UNIT_T UNARY OPERATORS
//------------------------------

// unary addition: +T
template<class Units, typename T, template<typename> class NonLinearScale>
inline unit_t<Units, T, NonLinearScale> operator+(const unit_t<Units, T, NonLinearScale>& u) noexcept
{
return u;
}

// prefix increment: ++T
template<class Units, typename T, template<typename> class NonLinearScale>
inline unit_t<Units, T, NonLinearScale>& operator++(unit_t<Units, T, NonLinearScale>& u) noexcept
{
u = unit_t<Units, T, NonLinearScale>(u() + 1);
return u;
}

// postfix increment: T++
template<class Units, typename T, template<typename> class NonLinearScale>
inline unit_t<Units, T, NonLinearScale> operator++(unit_t<Units, T, NonLinearScale>& u, int) noexcept
{
auto ret = u;
u = unit_t<Units, T, NonLinearScale>(u() + 1);
return ret;
}

// unary addition: -T
template<class Units, typename T, template<typename> class NonLinearScale>
inline unit_t<Units, T, NonLinearScale> operator-(const unit_t<Units, T, NonLinearScale>& u) noexcept
{
return unit_t<Units, T, NonLinearScale>(-u());
}

// prefix increment: --T
template<class Units, typename T, template<typename> class NonLinearScale>
inline unit_t<Units, T, NonLinearScale>& operator--(unit_t<Units, T, NonLinearScale>& u) noexcept
{
u = unit_t<Units, T, NonLinearScale>(u() - 1);
return u;
}

// postfix increment: T--
template<class Units, typename T, template<typename> class NonLinearScale>
inline unit_t<Units, T, NonLinearScale> operator--(unit_t<Units, T, NonLinearScale>& u, int) noexcept
{
auto ret = u;
u = unit_t<Units, T, NonLinearScale>(u() - 1);
return ret;
}

//------------------------------
// UNIT_CAST
//------------------------------
Expand Down
1 change: 1 addition & 0 deletions unitTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAN

TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIBRARIES})
IF(WIN32)
add_definitions(/D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/machine:x64")
ENDIF(WIN32)

Expand Down
Loading

4 comments on commit 3252189

@JohelEGP
Copy link
Contributor

@JohelEGP JohelEGP commented on 3252189 May 9, 2018

Choose a reason for hiding this comment

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

std::numeric_limits is for arithmetic types or types emulating those. Even std::chrono provides no specializations for it. It also fails to meet its requirements. It might be useful for somebody someday, so maybe you'll want to keep it for dimensionless units.

@nholthaus
Copy link
Owner Author

@nholthaus nholthaus commented on 3252189 May 10, 2018

Choose a reason for hiding this comment

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

In my mind, units are arithmetic types. Agree that they don't meed the definition for that, but they are in a spirit-of-the-law way. I think numeric_limits for units should meet the requirements (will fix for v3), and if I were amending the ISO std to talk about units I'd say something to the effect of

std::numeric_limits of unit types will be the same as the underlying type of the unit. It does not mean that all conversion will be possible within those limits.

... only much better worded.

@JohelEGP
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe I'm being too pedantic. Following from your amend, the correct implementation would simply derive from std::numeric_limits<T>.

@nholthaus
Copy link
Owner Author

Choose a reason for hiding this comment

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

yeah, that sounds good

Please sign in to comment.