* 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 e26fcd7d9b.

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

This reverts commit d42020e1de.

* 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
This commit is contained in:
Nic Holthaus
2018-04-21 15:59:22 -04:00
committed by GitHub
parent 2342332025
commit 3252189fbe
8 changed files with 269 additions and 39 deletions

View File

@@ -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

View File

@@ -7,6 +7,28 @@ 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
@@ -14,9 +36,10 @@ ExternalProject_Add(
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}
)
# ------------------------------------------------------------------------------------------------------------------------------
@@ -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)

View File

@@ -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)

27
CMakeSettings.json Normal file
View File

@@ -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": ""
}
]
}

View File

@@ -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

View File

@@ -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
{
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
{
namespace detail
{
template <typename T> std::string to_string(const T& t)
{
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> inline constexpr const char* name(const T&);
template<typename T> inline constexpr const char* abbreviation(const T&);
}
//------------------------------
@@ -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; /** @} */\
}
/**
@@ -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.
@@ -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)
@@ -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>
@@ -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
{
@@ -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
//------------------------------

View File

@@ -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)

View File

@@ -965,6 +965,25 @@ TEST_F(UnitContainer, unitTypeAddition)
EXPECT_NEAR(2.0, d, 5.0e-6);
}
TEST_F(UnitContainer, unitTypeUnaryAddition)
{
meter_t a_m(1.0);
EXPECT_EQ(++a_m, meter_t(2));
EXPECT_EQ(a_m++, meter_t(2));
EXPECT_EQ(a_m, meter_t(3));
EXPECT_EQ(+a_m, meter_t(3));
EXPECT_EQ(a_m, meter_t(3));
dBW_t b_dBW(1.0);
EXPECT_EQ(++b_dBW, dBW_t(2));
EXPECT_EQ(b_dBW++, dBW_t(2));
EXPECT_EQ(b_dBW, dBW_t(3));
EXPECT_EQ(+b_dBW, dBW_t(3));
EXPECT_EQ(b_dBW, dBW_t(3));
}
TEST_F(UnitContainer, unitTypeSubtraction)
{
meter_t a_m(1.0), c_m;
@@ -998,6 +1017,25 @@ TEST_F(UnitContainer, unitTypeSubtraction)
EXPECT_NEAR(0.0, d, 5.0e-6);
}
TEST_F(UnitContainer, unitTypeUnarySubtraction)
{
meter_t a_m(4.0);
EXPECT_EQ(--a_m, meter_t(3));
EXPECT_EQ(a_m--, meter_t(3));
EXPECT_EQ(a_m, meter_t(2));
EXPECT_EQ(-a_m, meter_t(-2));
EXPECT_EQ(a_m, meter_t(2));
dBW_t b_dBW(4.0);
EXPECT_EQ(--b_dBW, dBW_t(3));
EXPECT_EQ(b_dBW--, dBW_t(3));
EXPECT_EQ(b_dBW, dBW_t(2));
EXPECT_EQ(-b_dBW, dBW_t(-2));
EXPECT_EQ(b_dBW, dBW_t(2));
}
TEST_F(UnitContainer, unitTypeMultiplication)
{
meter_t a_m(1.0), b_m(2.0);
@@ -1371,13 +1409,56 @@ TEST_F(UnitContainer, to_string)
EXPECT_STREQ("8 m", units::length::to_string(b).c_str());
}
TEST_F(UnitContainer, abbreviation)
TEST_F(UnitContainer, to_string_locale)
{
struct lconv * lc;
// German locale
#if defined(_MSC_VER)
setlocale(LC_ALL, "de-DE");
#else
EXPECT_STREQ("de_DE.utf8",setlocale(LC_ALL, "de_DE.utf8"));
#endif
lc = localeconv();
char point_de = *lc->decimal_point;
EXPECT_EQ(point_de, ',');
kilometer_t de = 2_km;
EXPECT_STREQ("2 km", units::length::to_string(de).c_str());
de = 2.5_km;
EXPECT_STREQ("2,5 km", units::length::to_string(de).c_str());
// US locale
#if defined(_MSC_VER)
setlocale(LC_ALL, "en-US");
#else
EXPECT_STREQ("en_US.utf8",setlocale(LC_ALL, "en_US.utf8"));
#endif
lc = localeconv();
char point_us = *lc->decimal_point;
EXPECT_EQ(point_us, '.');
mile_t us = 2_mi;
EXPECT_STREQ("2 mi", units::length::to_string(us).c_str());
us = 2.5_mi;
EXPECT_STREQ("2.5 mi", units::length::to_string(us).c_str());
}
TEST_F(UnitContainer, nameAndAbbreviation)
{
foot_t a(3.5);
EXPECT_STREQ("ft", units::length::abbreviation(a));
EXPECT_STREQ("ft", units::abbreviation(a));
EXPECT_STREQ("ft", a.abbreviation());
EXPECT_STREQ("foot", a.name());
meter_t b(8);
EXPECT_STREQ("m", units::length::abbreviation(b));
EXPECT_STREQ("m", units::abbreviation(b));
EXPECT_STREQ("m", b.abbreviation());
EXPECT_STREQ("meter", b.name());
}
#endif