mirror of https://github.com/orefkov/simstr.git
- Version updated to 1.6.0.
- Added functions e_concat and e_subst. - Added four ways to specify a type conversion to a string expression. - Implemented a universal operator+ for string expressions and types that can be converted to string expressions. - Версия обновлена на 1.6.0. - Добавлены функции e_concat и e_subst. - Добавлено четыре способа задать преобразование типа в строковое выражение. - Реализован универсальный operator+ для строковых выражений и типов, которые могут преобразовываться в строковые выражения.
This commit is contained in:
parent
35d2ea2ee8
commit
bb70d1b0a4
|
|
@ -5,7 +5,7 @@ include(FetchContent)
|
|||
|
||||
project(
|
||||
simstr
|
||||
VERSION 1.5.0
|
||||
VERSION 1.6.0
|
||||
DESCRIPTION "Yet another modern C++ string library"
|
||||
HOMEPAGE_URL "https://github.com/orefkov/simstr"
|
||||
LANGUAGES CXX
|
||||
|
|
|
|||
|
|
@ -52,10 +52,22 @@ void ConcatSimToSim(benchmark::State& state) {
|
|||
}
|
||||
}
|
||||
|
||||
void ConcatSimToSimConcat(benchmark::State& state) {
|
||||
stra s1 = "start ";
|
||||
for (auto _: state) {
|
||||
for (int i = 1; i <= 100'000; i *= 10) {
|
||||
benchmark::DoNotOptimize(s1);
|
||||
stringa str = e_concat("", s1, i, " end");
|
||||
benchmark::DoNotOptimize(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(__)->Name("----- Concatenate string + Number + \"Literal\" ---------")->Repetitions(1);
|
||||
BENCHMARK(ConcatStdToStd) ->Name("Concat std::string and number by std to std::string");
|
||||
BENCHMARK(ConcatSimToStd) ->Name("Concat std::string and number by StrExpr to std::string");
|
||||
BENCHMARK(ConcatSimToSim) ->Name("Concat stringa and number by StrExpr to simstr::stringa");
|
||||
BENCHMARK(ConcatSimToSimConcat) ->Name("Concat stringa and number by e_concat to simstr::stringa");
|
||||
|
||||
void ConcatStdToStdHex(benchmark::State& state) {
|
||||
// We use a short string so that the longest result is 15 characters and fits in the std::string SSO buffer.
|
||||
|
|
@ -64,7 +76,7 @@ void ConcatStdToStdHex(benchmark::State& state) {
|
|||
for (unsigned i = 1; i <= 100'000; i *= 10) {
|
||||
benchmark::DoNotOptimize(s1);
|
||||
// What is standard method to get hex number?
|
||||
std::string str = s1 + std::format("0x{:x}", i) + " end";
|
||||
std::string str = std::format("{}0x{:x} end", s1, i);
|
||||
benchmark::DoNotOptimize(str);
|
||||
}
|
||||
}
|
||||
|
|
@ -93,10 +105,36 @@ void ConcatSimToSimHex(benchmark::State& state) {
|
|||
}
|
||||
}
|
||||
|
||||
void ConcatSimToSimHexC(benchmark::State& state) {
|
||||
// stringa SSO buffer is 23, but we use a short string to compare under the same conditions
|
||||
stra s1 = "art ";
|
||||
for (auto _: state) {
|
||||
for (unsigned i = 1; i <= 100'000; i *= 10) {
|
||||
benchmark::DoNotOptimize(s1);
|
||||
stringa str = e_concat("", s1, e_hex<HexFlags::Short>(i), " end");
|
||||
benchmark::DoNotOptimize(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConcatSimToSimHexS(benchmark::State& state) {
|
||||
// stringa SSO buffer is 23, but we use a short string to compare under the same conditions
|
||||
stra s1 = "art ";
|
||||
for (auto _: state) {
|
||||
for (unsigned i = 1; i <= 100'000; i *= 10) {
|
||||
benchmark::DoNotOptimize(s1);
|
||||
stringa str = e_subst(S_FRM("{}{} end"), s1, e_hex<HexFlags::Short>(i));
|
||||
benchmark::DoNotOptimize(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(__)->Name("----- Concatenate string + Hex Number + \"Literal\" ---------")->Repetitions(1);
|
||||
BENCHMARK(ConcatStdToStdHex) ->Name("Concat std::string and hex number by std to std::string");
|
||||
BENCHMARK(ConcatSimToStdHex) ->Name("Concat std::string and hex number by StrExpr to std::string");
|
||||
BENCHMARK(ConcatSimToSimHex) ->Name("Concat stringa and hex number by StrExpr to simstr::stringa");
|
||||
BENCHMARK(ConcatSimToSimHexC) ->Name("Concat stringa and hex number by e_concat to simstr::stringa");
|
||||
BENCHMARK(ConcatSimToSimHexS) ->Name("Concat stringa and hex number by e_subst to simstr::stringa");
|
||||
|
||||
void ConcatStdToStdS(benchmark::State& state) {
|
||||
std::string s1 = "start ";
|
||||
|
|
@ -205,7 +243,6 @@ BENCHMARK(FindConcatThreeStr)->Name("Find concat three std::string");
|
|||
BENCHMARK(FindConcatThreeExp)->Name("Find concat three strexpr");
|
||||
BENCHMARK(FindConcatThreeSim)->Name("Find concat three simstr");
|
||||
|
||||
|
||||
std::string buildTypeNameStr(std::string_view type_name, size_t prec, size_t scale) {
|
||||
std::string res{type_name};
|
||||
if (prec) {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
|
|||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.5.0
|
||||
PROJECT_NUMBER = 1.6.0
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewers a
|
||||
|
|
@ -992,7 +992,7 @@ WARN_LOGFILE =
|
|||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../src ../include
|
||||
INPUT = ../src ../include ../tests
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
|
@ -1127,7 +1127,7 @@ EXCLUDE_SYMBOLS =
|
|||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATH = ../tests
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ PROJECT_NAME = "simstr"
|
|||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.5.0
|
||||
PROJECT_NUMBER = 1.6.0
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewers a
|
||||
|
|
@ -992,7 +992,7 @@ WARN_LOGFILE =
|
|||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../src ../include
|
||||
INPUT = ../src ../include ../tests
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
|
@ -1127,7 +1127,7 @@ EXCLUDE_SYMBOLS =
|
|||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATH = ../tests
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||||
* ver. 1.5.0
|
||||
* ver. 1.6.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||||
* ver. 1.5.0
|
||||
* ver. 1.6.0
|
||||
* Классы для работы со строками
|
||||
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
|
||||
* ver. 1.5.0
|
||||
* ver. 1.6.0
|
||||
* Classes for working with strings
|
||||
*/
|
||||
|
||||
|
|
@ -48,7 +48,6 @@ const bool isWindowsOs = // NOLINT
|
|||
|
||||
#define IN_FULL_SIMSTR
|
||||
#include "strexpr.h"
|
||||
#undef simple_str
|
||||
|
||||
#include <format>
|
||||
#include <unordered_map>
|
||||
|
|
@ -3374,9 +3373,6 @@ protected:
|
|||
const K* sstr_;
|
||||
};
|
||||
size_t bigLen_; // Длина не локальной строки | Non-local string length
|
||||
uns_type pad_[LocalCount - (sizeof(const K*) + sizeof(size_t)) / sizeof(K)];
|
||||
uns_type blocalRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
|
||||
uns_type btype_ : 2;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -3385,7 +3381,7 @@ protected:
|
|||
localRemain_ = LocalCount;
|
||||
buf_[0] = 0;
|
||||
}
|
||||
K* init(size_t s) {
|
||||
constexpr K* init(size_t s) {
|
||||
if (s > LocalCount) {
|
||||
type_ = Shared;
|
||||
localRemain_ = 0;
|
||||
|
|
@ -3472,7 +3468,7 @@ public:
|
|||
*/
|
||||
template<typename... Args>
|
||||
requires std::is_constructible_v<allocator_t, Args...>
|
||||
sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...), buf_{0} {
|
||||
sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||||
base_storable::init_from_str_other(other);
|
||||
}
|
||||
/*!
|
||||
|
|
@ -3550,7 +3546,7 @@ public:
|
|||
static const sstring<K> empty_str;
|
||||
/// @ru Деструктор строки. @en String destructor.
|
||||
constexpr ~sstring() {
|
||||
if (btype_ == Shared) {
|
||||
if (type_ == Shared) {
|
||||
SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
|
||||
}
|
||||
}
|
||||
|
|
@ -3635,13 +3631,11 @@ public:
|
|||
*/
|
||||
template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
|
||||
requires std::is_constructible_v<allocator_t, Args...>
|
||||
constexpr sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...)
|
||||
, btype_(Constant)
|
||||
, blocalRemain_(0)
|
||||
, cstr_(s)
|
||||
, bigLen_(N - 1)
|
||||
, pad_{}
|
||||
{
|
||||
sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
|
||||
type_ = Constant;
|
||||
localRemain_ = 0;
|
||||
cstr_ = s;
|
||||
bigLen_ = N - 1;
|
||||
}
|
||||
|
||||
constexpr void swap(my_type&& other) noexcept {
|
||||
|
|
@ -3738,11 +3732,11 @@ public:
|
|||
}
|
||||
/// @ru Указатель на символы строки. @en Pointer to characters in the string.
|
||||
constexpr const K* symbols() const noexcept {
|
||||
return btype_ == Local ? buf_ : cstr_;
|
||||
return type_ == Local ? buf_ : cstr_;
|
||||
}
|
||||
/// @ru Длина строки. @en Line length.
|
||||
constexpr size_t length() const noexcept {
|
||||
return btype_ == Local ? LocalCount - blocalRemain_ : bigLen_;
|
||||
return type_ == Local ? LocalCount - localRemain_ : bigLen_;
|
||||
}
|
||||
/// @ru Пустая ли строка. @en Is the string empty?
|
||||
constexpr bool is_empty() const noexcept {
|
||||
|
|
@ -3802,9 +3796,11 @@ template<typename K, Allocatorable Allocator>
|
|||
inline const sstring<K> sstring<K, Allocator>::empty_str{};
|
||||
|
||||
template<typename K>
|
||||
consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
|
||||
consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<ubs> sb, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
|
||||
if constexpr (std::is_same_v<K, u8s>)
|
||||
return s8;
|
||||
if constexpr (std::is_same_v<K, ubs>)
|
||||
return sb;
|
||||
if constexpr (std::is_same_v<K, uws>)
|
||||
return sw;
|
||||
if constexpr (std::is_same_v<K, u16s>)
|
||||
|
|
@ -3813,7 +3809,7 @@ consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<uws>
|
|||
return s32;
|
||||
}
|
||||
|
||||
#define uni_string(K, p) select_str<K>(p, L##p, u##p, U##p)
|
||||
#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
|
||||
|
||||
template<typename K, typename H>
|
||||
struct StoreType {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
[](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
|
||||
|
||||
Version 1.5.0.
|
||||
Version 1.6.0.
|
||||
|
||||
<span class="obfuscator"><a href="readme_ru.md">On Russian | По-русски</a></span>
|
||||
|
||||
|
|
@ -302,8 +302,8 @@ function(add_simstr)
|
|||
simstr
|
||||
GIT_REPOSITORY https://github.com/orefkov/simstr.git
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_TAG tags/rel1.5.0 # Укажите нужный релиз
|
||||
FIND_PACKAGE_ARGS NAMES simstr 1.5.0
|
||||
GIT_TAG tags/rel1.6.0 # Укажите нужный релиз
|
||||
FIND_PACKAGE_ARGS NAMES simstr 1.6.0
|
||||
)
|
||||
FetchContent_MakeAvailable(simstr)
|
||||
endfunction()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
[](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
|
||||
|
||||
Версия 1.5.0.
|
||||
Версия 1.6.0.
|
||||
|
||||
<span class="obfuscator"><a href="readme.md">On English | По-английски</a></span>
|
||||
|
||||
|
|
@ -303,8 +303,8 @@ function(add_simstr)
|
|||
simstr
|
||||
GIT_REPOSITORY https://github.com/orefkov/simstr.git
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_TAG tags/rel1.5.0 # Укажите нужный релиз
|
||||
FIND_PACKAGE_ARGS NAMES simstr 1.5.0
|
||||
GIT_TAG tags/rel1.6.0 # Укажите нужный релиз
|
||||
FIND_PACKAGE_ARGS NAMES simstr 1.6.0
|
||||
)
|
||||
FetchContent_MakeAvailable(simstr)
|
||||
endfunction()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* ver. 1.5.0
|
||||
* ver. 1.6.0
|
||||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||||
* Реализация строковых функций
|
||||
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
|
||||
add_executable(test_str test_str.cpp)
|
||||
add_executable(test_expr_only test_expr_only.cpp)
|
||||
add_executable(test_expr_only test_expr_only.cpp test_tostrexpr.cpp)
|
||||
|
||||
target_link_libraries(test_str simstr::simstr GTest::gtest_main)
|
||||
target_link_libraries(test_expr_only GTest::gtest_main)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* ver. 1.5.0
|
||||
* ver. 1.6.0
|
||||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||||
* Тесты simstr
|
||||
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
|
||||
|
|
@ -62,6 +62,15 @@ TEST(StrExpr, Spaces) {
|
|||
EXPECT_EQ(testu, u"abc__________cde");
|
||||
}
|
||||
|
||||
TEST(StrExpr, PlusString) {
|
||||
std::string t = "aa"_ss + "bb"s;
|
||||
EXPECT_EQ(t, "aabb");
|
||||
const std::string add = "bb";
|
||||
std::string r = add + "aa"_ss;
|
||||
EXPECT_EQ(r, "bbaa");
|
||||
|
||||
}
|
||||
|
||||
TEST(StrExpr, Repeat) {
|
||||
std::string testa = "abc";
|
||||
testa = e_repeat(+testa + " " + 10 + "s.", 3);
|
||||
|
|
@ -142,7 +151,7 @@ TEST(StrExpr, Join) {
|
|||
TEST(StrExpr, Replace) {
|
||||
std::string testa = e_repl("test"_ss, "t"sv, "-|-"s) + 10;
|
||||
EXPECT_EQ(testa, "-|-es-|-10");
|
||||
testa = e_repl("aaaaaaaaaaaaaaaa"_ss, "a", "bb");
|
||||
testa = e_repl("aaaaaaaaaaaaaaaa", "a", "bb");
|
||||
EXPECT_EQ(testa, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
||||
|
||||
testa = e_repl("aaaaaaaaaaaaaaaa"_ss, "a", "") + "-";
|
||||
|
|
@ -302,4 +311,27 @@ TEST(StrExpr, StrReplace) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(StrExpr, Concat) {
|
||||
std::string tt;
|
||||
std::string c = e_concat(eea + "," + " ", u8"bb", tt, 1, e_if(true, "--"), e_hex<HexFlags::Short>(16), 1.2);
|
||||
EXPECT_EQ(c, "bb, , 1, --, 0x10, 1.2");
|
||||
|
||||
std::string_view text = "testes";
|
||||
int count = 10;
|
||||
std::string t = e_concat("", text, " = ", count, " times.");
|
||||
EXPECT_EQ(t, "testes = 10 times.");
|
||||
}
|
||||
|
||||
TEST(StrExpr, Subst) {
|
||||
const auto ttt = "test"_ss;
|
||||
int ii = 3;
|
||||
std::string t = e_subst(S_FRM("Test {{--}} {}=, {}"), ttt, ii);
|
||||
EXPECT_EQ(t, "Test {--} test=, 3");
|
||||
t = e_subst(S_FRM("Test {2}={1}, {2}, {1}"), "test", 2);
|
||||
EXPECT_EQ(t, "Test 2=test, 2, test");
|
||||
|
||||
std::u16string u16t = e_subst(S_FRM(u"Test {}={}, {}"), u"test", 2, u"test"sv);
|
||||
EXPECT_EQ(u16t, u"Test test=2, test");
|
||||
}
|
||||
|
||||
} // namespace simstr::tests
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* ver. 1.5.0
|
||||
* ver. 1.6.0
|
||||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||||
* Тесты simstr
|
||||
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
|
||||
|
|
@ -1797,17 +1797,6 @@ TEST(SimStr, ExprRepeat) {
|
|||
EXPECT_EQ(std::string{e_repeat("aa"_ss + t + "_", 3)}, "aa1_aa1_aa1_");
|
||||
}
|
||||
|
||||
TEST(SimStr, Constexpr) {
|
||||
constexpr ssa tt = " asd "_ss.trimmed();
|
||||
static_assert(tt == "asd");
|
||||
constexpr stringa aa{"asd"};
|
||||
static_assert(aa == "asd");
|
||||
static_assert(aa.length() == 3);
|
||||
constexpr stringa bb = "";
|
||||
constexpr int k = "123"_ss.to_int<int>().value;
|
||||
static_assert(k == 123);
|
||||
}
|
||||
|
||||
TEST(SimStr, StrExpToStdString) {
|
||||
std::basic_string<u8s, std::char_traits<u8s>, std::pmr::polymorphic_allocator<u8s>> test = "count = "_ss + 10 + " times";
|
||||
EXPECT_EQ(test, "count = 10 times");
|
||||
|
|
@ -2003,6 +1992,13 @@ TEST(SimStr, EFill) {
|
|||
EXPECT_EQ(test, "t=10______>");
|
||||
}
|
||||
|
||||
TEST(SimStr, Subst) {
|
||||
int from = 1, total = 100;
|
||||
bool success = true;
|
||||
lstringu<100> u16t = e_subst(S_FRM(u"Test {} from {}, {}."), from, total, e_choice(success, u"success", u"fail"));
|
||||
EXPECT_EQ(u16t, u"Test 1 from 100, success.");
|
||||
}
|
||||
|
||||
} // namespace simstr::tests
|
||||
|
||||
TEST(SimStr, StrNoNamespace) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* ver. 1.6.0
|
||||
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
|
||||
* Тесты simstr
|
||||
* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
|
||||
* Test of simstr
|
||||
*/
|
||||
|
||||
#include "../include/simstr/strexpr.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <format>
|
||||
#include <list>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace simstr::tests {
|
||||
/*!
|
||||
* @ru
|
||||
* @defgroup ConvertToStrExpr Конвертация типов в в строковые выражения
|
||||
* Различные способы конвертации типов в строковые выражения.
|
||||
* Если вы реализуете один (и только один) из способов преобразования вашего типа в строковое выражение,
|
||||
* то сможете использовать ваш тип напрямую в операциях конкатенации со строковыми выражениями и как
|
||||
* аргументы в функциях `e_concat` и `e_subst`.
|
||||
* @en
|
||||
* @defgroup ConvertToStrExpr Converting types to string expressions
|
||||
* Various ways to convert types to string expressions.
|
||||
* If you implement one (and only one) of the ways to convert your type to a string expression,
|
||||
* then you can use your type directly in concatenation operations with string expressions and how
|
||||
* arguments in the `e_concat` and `e_subst` functions.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @ingroup ConvertToStrExpr
|
||||
* @ru @page method1 Способ 1
|
||||
* Просто реализуйте в своём типе требования, чтобы он являлся строковым выражением.
|
||||
* @en @page method1 Method 1
|
||||
* Just implement the requirement in your type that it be a string expression.
|
||||
* @ru @par Пример:
|
||||
* @en @par Example:
|
||||
* @~
|
||||
* @snippet test_tostrexpr.cpp Method1
|
||||
*/
|
||||
|
||||
//! [Method1]
|
||||
struct add_exclamation {
|
||||
ssa text_;
|
||||
unsigned count_;
|
||||
|
||||
// symb_type
|
||||
using symb_type = char;
|
||||
// length
|
||||
size_t length() const noexcept {
|
||||
return text_.length() + count_;
|
||||
}
|
||||
// place
|
||||
char* place(char* ptr) const noexcept{
|
||||
ptr = text_.place(ptr);
|
||||
std::char_traits<char>::assign(ptr, count_, '!');
|
||||
return ptr + count_;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ToStrExpr, CheckExclamation) {
|
||||
std::string test = "Msg is <" + add_exclamation{"Happy Birthday", 5} + ">";
|
||||
EXPECT_EQ(test, "Msg is <Happy Birthday!!!!!>");
|
||||
|
||||
add_exclamation msg{"Happy Birthday", 3};
|
||||
test = e_concat("", "Msg is <", msg, ">");
|
||||
EXPECT_EQ(test, "Msg is <Happy Birthday!!!>");
|
||||
|
||||
const add_exclamation cmsg{"Happy Birthday", 0};
|
||||
test = e_subst(S_FRM("Msg is <{}>"), cmsg);
|
||||
EXPECT_EQ(test, "Msg is <Happy Birthday>");
|
||||
}
|
||||
//! [Method1]
|
||||
|
||||
/*!
|
||||
* @ingroup ConvertToStrExpr
|
||||
* @ru @page method2 Способ 2
|
||||
* Создайте тип-обёртку, который является строковым выражением и может инициализироваться
|
||||
* вашим типом. После задайте их соответствие с помощью специализации шаблона `convert_to_strexpr`.
|
||||
* @en @page method2 Method 2
|
||||
* Create a wrapper type that is a string expression and can be initialized
|
||||
* your type. Then set their correspondence using the `convert_to_strexpr` template specialization.
|
||||
* @ru @par Пример:
|
||||
* @en @par Example:
|
||||
* @~
|
||||
* @snippet test_tostrexpr.cpp Method2
|
||||
*/
|
||||
|
||||
//! [Method2]
|
||||
|
||||
// Тип / Type
|
||||
struct car_info {
|
||||
std::string model;
|
||||
int year;
|
||||
};
|
||||
|
||||
// Обёртка для превращения его в строковое выражение
|
||||
// Wrapper to turn it into a string expression
|
||||
struct car_info_expr {
|
||||
const car_info& car_;
|
||||
expr_num<char, int> year;
|
||||
|
||||
car_info_expr(const car_info& car) : car_(car), year(car.year){}
|
||||
inline static constexpr ssa ModelTag = "Model: ";
|
||||
inline static constexpr ssa YearTag = ", Year: ";
|
||||
|
||||
using symb_type = char;
|
||||
|
||||
size_t length() const {
|
||||
return ModelTag.length() + car_.model.length() + YearTag.length() + year.length();
|
||||
}
|
||||
|
||||
char* place(char* ptr) const {
|
||||
ptr = ModelTag.place(ptr);
|
||||
std::char_traits<char>::copy(ptr, car_.model.data(), car_.model.length());
|
||||
ptr += car_.model.length();
|
||||
ptr = YearTag.place(ptr);
|
||||
return year.place(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace simstr::tests
|
||||
|
||||
|
||||
namespace simstr {
|
||||
|
||||
// Специализируем шаблон, задавая соответствие типа и его обёртки
|
||||
// Specialize the template by specifying a match between the type and its wrapper
|
||||
template<>
|
||||
struct convert_to_strexpr<char, tests::car_info> {
|
||||
// Указываем тип, который будет "обёрткой" для нашего типа
|
||||
// Specify the type that will be a "wrapper" for our type
|
||||
using type = tests::car_info_expr;
|
||||
};
|
||||
|
||||
} // namespace simstr
|
||||
|
||||
namespace simstr::tests {
|
||||
|
||||
TEST(ToStrExpr, CheckCarInfo) {
|
||||
car_info ci{"Ford", 2020};
|
||||
std::string test = "Car is <"_ss + ci + ">";
|
||||
EXPECT_EQ(test, "Car is <Model: Ford, Year: 2020>");
|
||||
|
||||
ci.year++;
|
||||
test = e_concat("", "Car is <", ci, ">");
|
||||
EXPECT_EQ(test, "Car is <Model: Ford, Year: 2021>");
|
||||
|
||||
ci.year++;
|
||||
test = e_subst(S_FRM("Car is <{}>"), ci);
|
||||
EXPECT_EQ(test, "Car is <Model: Ford, Year: 2022>");
|
||||
}
|
||||
//! [Method2]
|
||||
|
||||
/*!
|
||||
* @ingroup ConvertToStrExpr
|
||||
* @ru @page method3 Способ 3
|
||||
* Специализируйте шаблон `convert_to_strexpr` для вашего типа и создайте в нём статическую
|
||||
* функцию `convert`, принимающую ваш объект и возвращающую строковое выражение, строковый объект
|
||||
* simstr или std::basic_string.
|
||||
* @en @page method3 Method 3
|
||||
* Specialize the `convert_to_strexpr` template for your type and create a static
|
||||
* `convert` function in it that takes your object and returns a string expression, a simstr string object,
|
||||
* or std::basic_string.
|
||||
* @ru @par Пример:
|
||||
* @en @par Example:
|
||||
* @~
|
||||
* @snippet test_tostrexpr.cpp Method3
|
||||
*/
|
||||
|
||||
//! [Method3]
|
||||
|
||||
struct animal {
|
||||
std::string name_;
|
||||
std::string sound_;
|
||||
};
|
||||
|
||||
} //namespace simstr::tests
|
||||
|
||||
namespace simstr {
|
||||
|
||||
// Специализируем шаблон, задавая соответствие типа и его обёртки
|
||||
// Specialize the template by specifying a match between the type and its wrapper
|
||||
template<>
|
||||
struct convert_to_strexpr<char, tests::animal> {
|
||||
// Создаём функцию `convert`, которая вернёт строковое представление объекта
|
||||
// Create a function `convert`, that will return a string representation of the object
|
||||
static auto convert(const tests::animal& a) {
|
||||
// Так делать нельзя - операция + складывает в выражение ссылки на временные объекты,
|
||||
// которые разрушаются после ";", и возвращать такой объект нельзя.
|
||||
// This can't be done - the operator+ adds references to temporary objects to the expression,
|
||||
// which are destroyed after ";", and such an object cannot be returned.
|
||||
//return "Animal: " + a.name_ + ", Sound: " + a.sound_;
|
||||
|
||||
// А вот такой можно - он не хранит ссылки на временные объекты
|
||||
// But this one is possible - it doesn't store references to temporary objects
|
||||
return e_concat("", "Animal: ", a.name_, ", Sound: ", a.sound_);
|
||||
// Также можно возвращать просто std::string, stringa, lstringa
|
||||
// You can also return just std::string, stringa, lstringa
|
||||
}
|
||||
};
|
||||
|
||||
} //namespace simstr
|
||||
|
||||
namespace simstr::tests {
|
||||
|
||||
TEST(ToStrExpr, CheckAnimal) {
|
||||
animal cat{"Cat", "Meow"};
|
||||
|
||||
std::string test = "<"_ss + cat + ">";
|
||||
EXPECT_EQ(test, "<Animal: Cat, Sound: Meow>");
|
||||
|
||||
const animal dog{"Dog", "Woof"};
|
||||
test = e_concat("", "<", dog, ">");
|
||||
EXPECT_EQ(test, "<Animal: Dog, Sound: Woof>");
|
||||
|
||||
test = e_subst(S_FRM("\\_{}_/"), animal{"Snake", "Pssstt"});
|
||||
EXPECT_EQ(test, "\\_Animal: Snake, Sound: Pssstt_/");
|
||||
}
|
||||
//! [Method3]
|
||||
|
||||
/*!
|
||||
* @ingroup ConvertToStrExpr
|
||||
* @ru @page method4 Способ 4
|
||||
* Просто в своём типе сделайте функцию `template<typename K> auto to_strexpr()const`, которая возвращает
|
||||
* строковое выражение или строковый объект.
|
||||
* @en @page method4 Method 4
|
||||
* Simply create a `template<typename K> auto to_strexpr()const` function in your type that returns a
|
||||
* string expression or string object.
|
||||
* @ru @par Пример:
|
||||
* @en @par Example:
|
||||
* @~
|
||||
* @snippet test_tostrexpr.cpp Method4
|
||||
*/
|
||||
|
||||
//! [Method4]
|
||||
|
||||
struct test {
|
||||
int number;
|
||||
int from;
|
||||
// Сделаем две отдельные реализации: для char (попроще) и для остальных типов (там придётся возвращать не литералы, а simple_str, и требуется копия)
|
||||
// Let's make two separate implementations: for char (simpler) and for other types (there we'll have to return not literals, but simple_str, and a copy is required)
|
||||
template<typename K> requires (std::is_same_v<K, char>)
|
||||
auto to_strexpr() const {
|
||||
return e_concat("", "test ", number, " from ", from);
|
||||
}
|
||||
|
||||
template<typename K> requires (!std::is_same_v<K, char>)
|
||||
auto to_strexpr() const {
|
||||
// uni_string возвращает локальный объект, а ссылку на них нельзя возвращать из функции,
|
||||
// поэтому в e_concat надо форсировать сохранение по копии, а не по ссылке.
|
||||
// uni_string returns a local object, and references to them cannot be returned from a function,
|
||||
// so in e_concat we need to force saving by copy, not by reference.
|
||||
return e_concat(force_copy{empty_expr<K>{}}, force_copy{uni_string(K, "test ")}, number, force_copy{uni_string(K, " from ")}, from);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ToStrExpr, CheckTest) {
|
||||
test t1{1, 10};
|
||||
|
||||
std::string t = "Begin <"_ss + t1 + ">";
|
||||
EXPECT_EQ(t, "Begin <test 1 from 10>");
|
||||
|
||||
const test t2{8, 12};
|
||||
|
||||
t = e_concat("", "<", t2, ">");
|
||||
EXPECT_EQ(t, "<test 8 from 12>");
|
||||
|
||||
t = e_subst(S_FRM("<{}>"), test{99, 100});
|
||||
EXPECT_EQ(t, "<test 99 from 100>");
|
||||
|
||||
// Проверим работу для не char
|
||||
// Let's check the work for non-char
|
||||
std::wstring wt = L"aaa "_ss + test{99, 100};
|
||||
EXPECT_EQ(wt, L"aaa test 99 from 100");
|
||||
|
||||
std::u16string ut = u"aaa "_ss + test{99, 100};
|
||||
EXPECT_EQ(ut, u"aaa test 99 from 100");
|
||||
}
|
||||
//! [Method4]
|
||||
|
||||
TEST(ToStrExpr, ConcatCustom) {
|
||||
std::string tt;
|
||||
std::string c = e_concat(eea + "," + " ", u8"bb", tt, 1, e_if(true, "--"), e_hex<HexFlags::Short>(16), 1.2, test{10, 30});
|
||||
EXPECT_EQ(c, "bb, , 1, --, 0x10, 1.2, test 10 from 30");
|
||||
|
||||
std::string_view text = "testes";
|
||||
int count = 10;
|
||||
std::string t = e_concat("", text, " = ", count, " times.");
|
||||
EXPECT_EQ(t, "testes = 10 times.");
|
||||
}
|
||||
|
||||
TEST(ToStrExpr, SubstCustom) {
|
||||
const auto ttt = "test"_ss;
|
||||
int ii = 3;
|
||||
const test tr{10, 10};
|
||||
std::string t = e_subst(S_FRM("Test {{--}} {}={}, {}"), ttt, tr, ii);
|
||||
EXPECT_EQ(t, "Test {--} test=test 10 from 10, 3");
|
||||
t = e_subst(S_FRM("Test {2}={1}, {2}, {1}"), "test", 2);
|
||||
EXPECT_EQ(t, "Test 2=test, 2, test");
|
||||
|
||||
std::u16string u16t = e_subst(S_FRM(u"Test {}={}, {}"), u"test", 2, u"test"sv);
|
||||
EXPECT_EQ(u16t, u"Test test=2, test");
|
||||
}
|
||||
} // namespace simstr::tests
|
||||
Loading…
Reference in New Issue