Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype minimal icu integration #25

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Prototype minimal icu integration
* TODO : Non icu-based default
* Support more units
* Detect supported units at compile time
* Support long names
* ratio ?
  • Loading branch information
cor3ntin committed Oct 18, 2019
commit 847d390c6a73df6cb7eeb4e819e7cafd7f519307
4 changes: 3 additions & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ class UnitsConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = (
"range-v3/0.9.1@ericniebler/stable",
"Catch2/2.10.0@catchorg/stable"
"Catch2/2.10.0@catchorg/stable",
"icu/64.2@bincrafters/stable",
"fmt/5.3.0@bincrafters/stable"
)
scm = {
"type": "git",
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ target_compile_features(units INTERFACE cxx_std_20)
target_link_libraries(units
INTERFACE
CONAN_PKG::range-v3
CONAN_PKG::fmt
CONAN_PKG::icu
)

target_include_directories(units
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
Expand Down
40 changes: 40 additions & 0 deletions src/include/units/bits/fmt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include "unicode/uchriter.h"
#include <fmt/format.h>
#include <unicode/measfmt.h>
#include <unicode/measunit.h>
#include <limits.h>
#include <cuchar>
#include <locale>

namespace units::details {

template<typename FormatContext>
auto format_localized_unit(const icu::Measure& m, FormatContext& ctx)
{
auto locale = ctx.locale().template get<std::locale>();

UErrorCode uc = {};
icu::MeasureFormat uFmt(locale.name() == "*" ? nullptr : locale.name().c_str(), UMEASFMT_WIDTH_SHORT, uc);
icu::UnicodeString s;
icu::FieldPosition p(icu::FieldPosition::DONT_CARE);
uFmt.formatMeasures(&m, 1, s, p, uc);
char out[MB_LEN_MAX]{};
icu::UCharCharacterIterator iter(s.getTerminatedBuffer(), s.length());
while(iter.hasNext()) {
std::mbstate_t state{};
auto c = iter.next32PostInc();
std::size_t rc = std::c32rtomb(out, c, &state);
if(rc != std::size_t(-1)) std::copy_n(std::begin(out), rc, ctx.out());
}
return ctx.out();
}

template<typename T>
icu::MeasureUnit* create_icu_unit(UErrorCode&)
{
return nullptr;
}

} // namespace units::details
9 changes: 9 additions & 0 deletions src/include/units/dimensions/acceleration.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#pragma once

#include <units/bits/fmt.h>
#include <units/dimensions/velocity.h>

namespace units {
Expand All @@ -41,4 +42,12 @@ namespace units {

} // namespace literals

namespace details {
template<>
inline icu::MeasureUnit* create_icu_unit<metre_per_second_sq>(UErrorCode& uc)
{
return icu::MeasureUnit::createMeterPerSecondSquared(uc);
}
} // namespace details

} // namespace units
32 changes: 31 additions & 1 deletion src/include/units/dimensions/area.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#pragma once

#include <units/dimensions/length.h>
#include <units/bits/fmt.h>

namespace units {

Expand Down Expand Up @@ -61,4 +62,33 @@ namespace units {

} // namespace literals

} // namespace units

namespace details {

template<>
inline icu::MeasureUnit* create_icu_unit<square_metre>(UErrorCode& uc)
{
return icu::MeasureUnit::createSquareMeter(uc);
}

template<>
inline icu::MeasureUnit* create_icu_unit<square_centimetre>(UErrorCode& uc)
{
return icu::MeasureUnit::createSquareCentimeter(uc);
}

template<>
inline icu::MeasureUnit* create_icu_unit<square_kilometre>(UErrorCode& uc)
{
return icu::MeasureUnit::createSquareKilometer(uc);
}

template<>
inline icu::MeasureUnit* create_icu_unit<square_foot>(UErrorCode& uc)
{
return icu::MeasureUnit::createSquareFoot(uc);
}

} // namespace details

} // namespace units
9 changes: 9 additions & 0 deletions src/include/units/dimensions/current.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,13 @@ namespace units {

}


namespace details {
template<>
inline icu::MeasureUnit* create_icu_unit<ampere>(UErrorCode& uc)
{
return icu::MeasureUnit::createAmpere(uc);
}
} // namespace details

} // namespace units
16 changes: 16 additions & 0 deletions src/include/units/dimensions/energy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <units/dimensions/base_dimensions.h>
#include <units/dimensions/force.h>
#include <units/dimensions/pressure.h>
#include <units/bits/fmt.h>

namespace units {

Expand Down Expand Up @@ -63,4 +64,19 @@ namespace units {

} // namespace literals

namespace details {

template<>
inline icu::MeasureUnit* create_icu_unit<joule>(UErrorCode& uc)
{
return icu::MeasureUnit::createJoule(uc);
}

template<>
inline icu::MeasureUnit* create_icu_unit<kilojoule>(UErrorCode& uc)
{
return icu::MeasureUnit::createKilojoule(uc);
}
} // namespace details

} // namespace units
9 changes: 9 additions & 0 deletions src/include/units/dimensions/force.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@ namespace units {

} // namespace literals


namespace details {
template<>
inline icu::MeasureUnit* create_icu_unit<newton>(UErrorCode& uc)
{
return icu::MeasureUnit::createNewton(uc);
}
} // namespace details

} // namespace units
30 changes: 30 additions & 0 deletions src/include/units/dimensions/frequency.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#pragma once

#include <units/bits/fmt.h>
#include <units/dimensions/base_dimensions.h>
#include <units/dimensions/time.h>

Expand Down Expand Up @@ -67,4 +68,33 @@ namespace units {

} // namespace literals


namespace details {

template<>
inline icu::MeasureUnit* create_icu_unit<hertz>(UErrorCode& uc)
{
return icu::MeasureUnit::createHertz(uc);
}

template<>
inline icu::MeasureUnit* create_icu_unit<kilohertz>(UErrorCode& uc)
{
return icu::MeasureUnit::createKilohertz(uc);
}

template<>
inline icu::MeasureUnit* create_icu_unit<megahertz>(UErrorCode& uc)
{
return icu::MeasureUnit::createMegahertz(uc);
}

template<>
inline icu::MeasureUnit* create_icu_unit<gigahertz>(UErrorCode& uc)
{
return icu::MeasureUnit::createGigahertz(uc);
}

} // namespace details

} // namespace units
4 changes: 4 additions & 0 deletions src/include/units/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
#pragma once

#include <units/quantity.h>
#include <fmt/format.h>
#include <unicode/measunit.h>
#include <unicode/measfmt.h>
#include <unicode/measure.h>

namespace units {

Expand Down
25 changes: 25 additions & 0 deletions src/include/units/quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@

#pragma once

#include <units/bits/fmt.h>
#include <units/bits/concepts.h>
#include <units/prefix.h>
#include <limits>
#include <ostream>
#include <unicode/measunit.h>


namespace units {

Expand Down Expand Up @@ -471,3 +474,25 @@ namespace units {
}

} // namespace units

namespace fmt {
template<typename Qty, typename T>
struct formatter<units::quantity<Qty, T>> {
template<typename ParseContext>
constexpr auto parse(ParseContext& ctx)
{
return ctx.begin();
}

template<typename FormatContext>
auto format(const units::quantity<Qty, T>& sm, FormatContext& ctx)
{
auto locale = ctx.locale().template get<std::locale>();

UErrorCode uc;
icu::MeasureUnit* unit = units::details::create_icu_unit<Qty>(uc);
icu::Measure m(sm.count(), unit, uc);
return units::details::format_localized_unit(m, ctx);
}
};
} // namespace fmt
1 change: 1 addition & 0 deletions test/unit_test/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_executable(unit_tests_runtime
digital_information_test.cpp
math_test.cpp
text_test.cpp
fmt_test.cpp
)
target_link_libraries(unit_tests_runtime
PRIVATE
Expand Down
8 changes: 7 additions & 1 deletion test/unit_test/runtime/catch_main.cpp
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
#define CATCH_CONFIG_MAIN
#define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp>

int main( int argc, char* argv[] ) {
setlocale(LC_CTYPE, "C.UTF-8");
int result = Catch::Session().run( argc, argv );
return result;
}
42 changes: 42 additions & 0 deletions test/unit_test/runtime/fmt_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "units/dimensions/area.h"
#include "units/dimensions/frequency.h"
#include "units/dimensions/power.h"
#include "units/dimensions/velocity.h"
#include "units/dimensions/volume.h"
#include "units/math.h"
#include <catch2/catch.hpp>
#include <fmt/locale.h>

using namespace units;

TEST_CASE("fmt on a quantity", "[text][fmt]")
{
SECTION("quantity with a predefined unit")
{
std::locale ru_loc("ru_RU.UTF-8");
REQUIRE(fmt::format(ru_loc, "{}", 2sq_m) == "2 м²");
REQUIRE(fmt::format("{}", 2GHz) == "2 GHz");
}
}