diff --git a/CMakeLists.txt b/CMakeLists.txt index e3499288..c4d4aad5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,24 +32,6 @@ set(GEN_STR_CATALOG ${CMAKE_CURRENT_LIST_DIR}/tools/gen_str_catalog.py CACHE FILEPATH "Location of script to generate a string catalog") -add_library(cib_sc INTERFACE) -target_compile_features(cib_sc INTERFACE cxx_std_20) -target_link_libraries_system(cib_sc INTERFACE fmt::fmt-header-only stdx) - -target_sources( - cib_sc - INTERFACE FILE_SET - sc - TYPE - HEADERS - BASE_DIRS - include - FILES - include/sc/format.hpp - include/sc/fwd.hpp - include/sc/lazy_string_format.hpp - include/sc/string_constant.hpp) - add_library(cib_match INTERFACE) target_compile_features(cib_match INTERFACE cxx_std_20) target_link_libraries_system(cib_match INTERFACE stdx) @@ -241,7 +223,7 @@ target_sources( add_library(cib_flow INTERFACE) target_compile_features(cib_flow INTERFACE cxx_std_20) -target_link_libraries_system(cib_flow INTERFACE cib_log cib_nexus cib_sc stdx) +target_link_libraries_system(cib_flow INTERFACE cib_log cib_nexus stdx) target_sources( cib_flow @@ -266,14 +248,7 @@ target_sources( add_library(cib_seq INTERFACE) target_compile_features(cib_seq INTERFACE cxx_std_20) -target_link_libraries_system( - cib_seq - INTERFACE - cib_flow - cib_log - cib_nexus - cib_sc - stdx) +target_link_libraries_system(cib_seq INTERFACE cib_flow cib_log cib_nexus stdx) target_sources( cib_seq @@ -303,7 +278,6 @@ target_link_libraries_system( cib_match cib_msg cib_nexus - cib_sc cib_seq concurrency fmt::fmt-header-only @@ -333,7 +307,6 @@ if(PROJECT_IS_TOP_LEVEL) clang_tidy_interface(cib_match) clang_tidy_interface(cib_msg) clang_tidy_interface(cib_nexus) - clang_tidy_interface(cib_sc) clang_tidy_interface(cib_seq) # Enable functional and performance test suites. diff --git a/docs/index.adoc b/docs/index.adoc index f8654892..6f3b4a23 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -13,5 +13,4 @@ include::logging.adoc[] include::interrupts.adoc[] include::match.adoc[] include::message.adoc[] -include::sc.adoc[] include::implementation_details.adoc[] diff --git a/docs/intro.adoc b/docs/intro.adoc index 6aa39b65..4073f3de 100644 --- a/docs/intro.adoc +++ b/docs/intro.adoc @@ -47,7 +47,6 @@ _cib_ contains several parts that work together: - xref:interrupt.adoc#_the_interrupt_library[`interrupt`] - xref:match.adoc#_the_match_library[`match`] - xref:message.adoc#_the_message_library[`message`] -- xref:sc.adoc#_the_sc_string_constant_library[`sc` (string constant)] === Sub-library DAG @@ -58,13 +57,11 @@ library that contains all the functionality. ---- flowchart BT nexus(cib_nexus) - sc(cib_sc) log(cib_log) match(cib_match) lookup(cib_lookup) flow(cib_flow) --> nexus - flow --> sc flow --> log log_fmt(cib_log_fmt) --> log diff --git a/docs/logging.adoc b/docs/logging.adoc index aa8e2f0d..444711f9 100644 --- a/docs/logging.adoc +++ b/docs/logging.adoc @@ -99,7 +99,7 @@ xref:logging.adoc#_logging_environments[environment]. The first two runtime parameters receive preprocessor `\_​_FILE_​\_` and `__LINE_​_` values respectively. The `msg` argument is a structure containing a -xref:sc.adoc#_formatting_strings[compile-time format string] and runtime +compile-time format string and runtime arguments to be interpolated into it. It supports an `apply` function, so one way to implement `log` is: @@ -185,11 +185,52 @@ struct my_struct { } ---- -=== String data - -On a constrained system, space for text can be at a premium. The `sc` library -and the MIPI Sys-T logger combine to -xref:sc.adoc#_efficient_logging_with_mipi_sys_t[solve this problem]. +=== Efficient logging with MIPI Sys-T + +On a constrained system, space for text can be limited-to-nonexistent. `cib` +uses `stdx::ct_format` and the +https://github.com/intel/compile-time-init-build/tree/main/include/log/catalog/mipi_encoder.hpp[MIPI +Sys-T logging config] to solve this problem. + +- First, each string constant contains string character data in its type. +- The MIPI logger calls the function template specialization + https://github.com/intel/compile-time-init-build/blob/main/include/log/catalog/catalog.hpp[`catalog`] + to get the catalog ID corresponding to each string constant. + +But: the `catalog` function template is just that -- only a template -- to +begin with. It is specialized as follows: + +- The application is built as a library. +- Running `nm` on that library reveals missing symbols: precisely the function + specializations that are required for all the string constants. +- Those symbols are used to generate the template specializations in another + file, which itself is compiled into a library. +- String data is recovered from the symbol types and used to generate the + catalog collateral in XML and/or JSON format. +- Link-time optimization inlines the `catalog` function template + specializations, each of which is a one-line function that returns a + catalog ID. + +Thus no string data exists in the executable, but the correct catalog IDs are +used in logging, and the remote log handler can reconstitute the actual strings. +The XML and JSON collateral also contains information about any runtime +arguments that need to be interpolated into the string and whose values are sent +by the MIPI Sys-T logger after the catalog ID. + +==== Tooling support + +The process of generating log strings from the type information revealed by +missing symbols is automated by a +https://github.com/intel/compile-time-init-build/blob/main/tools/gen_str_catalog.py[python +script] provided and by a +https://github.com/intel/compile-time-init-build/blob/main/cmake/string_catalog.cmake[CMake +wrapper function (`gen_str_catalog`)] that drives the process. See +https://github.com/intel/compile-time-init-build/blob/main/test/CMakeLists.txt[the +test] that exercises that functionality for an example. + +NOTE: This process assigns IDs to both strings and +xref:logging.adoc#_modules[log modules]. `catalog` is specialized for catalog +IDs; `module` is specialized for module IDs. === Version logging diff --git a/docs/sc.adoc b/docs/sc.adoc deleted file mode 100644 index b5e5b29e..00000000 --- a/docs/sc.adoc +++ /dev/null @@ -1,125 +0,0 @@ - -== The `sc` (string constant) library - -See code at -https://github.com/intel/compile-time-init-build/tree/main/include/sc. -Everything in the sc library is in the `sc` namespace. - -=== String constants - -An `sc::string_constant` is a compile-time string in the same way that -`std::integral_constant` is a compile-time integral value: it carries the string -in the type as a pack of `char`​s. - -[source,cpp] ----- -template struct string_constant; ----- - -The easiest way to create a `string_constant` is using a user-defined literal: - -[source,cpp] ----- -constexpr auto hello_world = "Hello, world!"_sc; - -// the string data itself is available as a compile-time std::string_view -using std::string_view_literals; -static_assert(decltype(hello_world)::value == "Hello, world!"sv); ----- - -`string_constant` specializations can be compared (even though they are -different types), and `operator+` is defined for concatenation. - -[source,cpp] ----- -// comparisons use lexicographical ordering -static_assert("Alice"_sc < "Bob"_sc); - -static_assert("Hello,"_sc + " World!"_sc == "Hello, World!"_sc); ----- - -=== Efficient logging with MIPI Sys-T - -The reason `string_constant` exists is for efficient logging. On a constrained -system, space for text can be limited-to-nonexistent. `string_constant` -interacts with the -https://github.com/intel/compile-time-init-build/tree/main/include/log/catalog/encoder.hpp[MIPI -Sys-T logging config] to solve this problem. - -- First, each `string_constant` contains string character data in its type. -- The MIPI logger calls the function template specialization - https://github.com/intel/compile-time-init-build/blob/main/include/log/catalog/catalog.hpp[`catalog`] - to get the catalog ID corresponding to each `string_constant`. - -But: the `catalog` function template is just that -- only a template -- to -begin with. It is specialized as follows: - -- The application is built as a library. -- Running `nm` on that library reveals missing symbols: precisely the function - specializations that are required for all the instances of `string_constant`. -- Those symbols are used to generate the template specializations in another - file, which itself is compiled into a library. -- String data is recovered from the symbol types and used to generate the - catalog collateral in XML and/or JSON format. -- Link-time optimization inlines the `catalog` function template - specializations, each of which is a one-line function that returns a - catalog ID. - -Thus no string data exists in the executable, but the correct catalog IDs are -used in logging, and the remote log handler can reconstitute the actual strings. -The XML and JSON collateral also contains information about any runtime -arguments that need to be interpolated into the string and whose values are sent -by the MIPI Sys-T logger after the catalog ID. - -==== Tooling support - -The process of generating log strings from the type information revealed by -missing symbols is automated by a -https://github.com/intel/compile-time-init-build/blob/main/tools/gen_str_catalog.py[python -script] provided and by a -https://github.com/intel/compile-time-init-build/blob/main/cmake/string_catalog.cmake[CMake -wrapper function (`gen_str_catalog`)] that drives the process. See -https://github.com/intel/compile-time-init-build/blob/main/test/CMakeLists.txt[the -test] that exercises that functionality for an example. - -NOTE: This process assigns IDs to both strings and -xref:logging.adoc#_modules[log modules]. `catalog` is specialized for catalog -IDs; `module` is specialized for module IDs. - -=== Formatting strings - -`string_constants` are formatted at compile time using -https://github.com/fmtlib/fmt[fmt]. Helpers are available for compile-time -formatting of enum values, type names or integral constants: - -[source,cpp] ----- -// string constants themselves are interpolated at compile time -sc::format("My name is {}"_sc, "Alice"_sc); // "My name is Alice" - -// enum values are interpolated at compile time -sc::format("The max log level is {}"_sc, sc::enum_); // "The max log level is MAX" - -// integral constants likewise -sc::format("{} is a taxicab number"_sc, std::integral_constant{}); // "1729 is a taxicab number" - -// and names of types -sc::format("When in doubt, do as the {}s do"_sc, sc::type_); // "When in doubt, do as the ints do" ----- - -When using _cib_ xref:logging.adoc#_log_macros[log macros], any arguments that -can be formatted at compile-time are automatically interpolated into the -`string_constant`, leaving the rest as dynamic runtime arguments to -xref:logging.adoc#_implementing_a_logger[`log`]. - -[source,cpp] ----- -// here, 42 is interpolated at compile time -// (sc::int_<42> is an integral_constant) -sc::format("The answer is {}"_sc, sc::int_<42>); - -// here, 42 is a runtime argument -// (because C++ doesn't have constexpr function parameters) -constexpr auto answer = 42; -sc::format("The answer is {}"_sc, answer); ----- diff --git a/include/flow/graph_builder.hpp b/include/flow/graph_builder.hpp index 3d53af17..fc318a39 100644 --- a/include/flow/graph_builder.hpp +++ b/include/flow/graph_builder.hpp @@ -169,12 +169,10 @@ struct graph_builder { template constexpr static auto error_steps = - stdx::transform([](auto N) { return stdx::ct_string_from_type(N); }, - T{}) - .join(stdx::ct_string{""}, [](auto x, auto y) { - using namespace stdx::literals; - return x + ", "_cts + y; - }); + T{}.join(stdx::cts_t<"">{}, [](auto x, auto y) { + using namespace stdx::literals; + return x + ", "_ctst + y; + }); constexpr static void check_for_missing_nodes(auto nodes, auto mentioned_nodes) { diff --git a/include/flow/graphviz_builder.hpp b/include/flow/graphviz_builder.hpp index 38099276..5f9d5c55 100644 --- a/include/flow/graphviz_builder.hpp +++ b/include/flow/graphviz_builder.hpp @@ -20,14 +20,14 @@ struct graphviz_builder { nodes_t sinks{}; for_each( [&](Node const &) { - sources.insert(Node::name_t::value); - sinks.insert(Node::name_t::value); + sources.emplace(Node::name_t::value); + sinks.emplace(Node::name_t::value); }, nodes); for_each( [&](Edge const &) { - sinks.erase(Edge::source_t::name_t::value); - sources.erase(Edge::dest_t::name_t::value); + sinks.erase(std::string_view{Edge::source_t::name_t::value}); + sources.erase(std::string_view{Edge::dest_t::name_t::value}); }, edges); @@ -39,9 +39,9 @@ struct graphviz_builder { } for_each( [&](Edge const &) { - output += std::string{Edge::source_t::name_t::value}; + output += std::string_view{Edge::source_t::name_t::value}; output += " -> "; - output += std::string{Edge::dest_t::name_t::value}; + output += std::string_view{Edge::dest_t::name_t::value}; output += '\n'; }, edges); diff --git a/include/flow/step.hpp b/include/flow/step.hpp index c34ff995..03adba03 100644 --- a/include/flow/step.hpp +++ b/include/flow/step.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -18,10 +17,7 @@ template struct ct_node { using is_subgraph = void; - using type_t = - decltype(stdx::ct_string_to_type()); - using name_t = - decltype(stdx::ct_string_to_type()); + using name_t = stdx::cts_t; using func_t = F; constexpr static auto ct_type = Type; diff --git a/include/sc/README.md b/include/sc/README.md deleted file mode 100644 index f3ed7083..00000000 --- a/include/sc/README.md +++ /dev/null @@ -1,179 +0,0 @@ -# string_constant - -An `sc::string_constant` is a compile-time string in the same way -that `std::integral_constant` is a compile-time integral value. When firmware -needs to emit a log message, an integral ID is emitted instead of the string -data. This conserves both runtime and storage resources. When decoding the log -messages, a catalog file is needed to map the IDs back to their string values. - -There are two challenges with such a system. The first is assigning a unique ID -to each string value and the second is how to support rich string operations -like format and concatenation. - -The `sc::string_constant` library solves both of these challenges by -representing the value of the string in its type. The `sc::string_constant`'s -string template struct is parameterized by a list of chars representing the -string value as well as a tuple of zero or more dynamic arguments to be -formatted by a log decoder. - -The details of this implementation are provided in the Theory of Operation -section. - -[![Embedded Logging Case Study: From C to Shining C++ - Luke Valenty -CppNow 2022](https://img.youtube.com/vi/Dt0vx-7e_B0/0.jpg)](https://www.youtube.com/watch?v=Dt0vx-7e_B0) - - -## Usage - -### `_sc` user defined literal - -`sc::string_constant` uses a user-defined literal to make it easy to define a -string: - -```c++ -constexpr auto hello_world = "Hello, World!"_sc; -``` - -Note how the auto keyword is used for the string type. This is necessary because -the string's type changes depending on its value. It would be redundant and -burdensome to specify the exact type. - -Also note the `constexpr` keyword is used. `sc::string_constant`s are fully -`constexpr` and nearly all operations are performed at compile-time even without -the `constexpr` keyword. - -Because the string value is represented by its type, strings can be assigned as -type aliases or passed in as template parameters. - -```c++ -using HelloWorldType = decltype("Hello, World!"_sc); - -constexpr HelloWorldType helloWorld{}; - -static_assert(helloWorld == "Hello, World!"_sc); -``` - -### Concatenation - -Two strings can be joined together using the `+` operator: - -```c++ -static_assert("Hi "_sc + "Emily!"_sc == "Hi Emily!"_sc); -``` - -### Format - -`sc::string_constant`s can be formatted using a subset of python -or `fmt::format` -format specifiers. Formatting behavior is different depending on whether the -arguments are compile-time values or dynamic values. Compile-time values will be -formatted at compile time while dynamic values will only be formatted by tools -outside of firmware like a log decoder. - -```c++ -constexpr auto my_age = 6; -constexpr auto day_to_go = day_of_week::SATURDAY; -auto my_name = "Olivia"_sc; -using BirthdayParty = party; - -// use int_ to format an integer known at compile time -static_assert( - format("I am {} years old."_sc, sc::int_) == - "I am 6 years old."_sc); - -// use enum_ to format an enum value known at compile time -static_assert( - format("Let's go on {}."_sc, sc::enum_) == - "Let's go on SATURDAY."_sc); - -// strings can be used as format arguments as well -static_assert( - format("My name is {}."_sc, my_name) == - "My name is Olivia."_sc); - -// use type_ to get the string of a type which can then be used as a format argument -static_assert( - type_ == - "party"_sc); - -// multiple arguments can be formatted at once -static_assert( - format("My name is {} and I am {} years old."_sc, my_name, my_age) == - "My name is Olivia and I am 6 years old."_sc); - -// runtime arguments not known at compile-time can also be formatted. the dynamic values can be -// accessed at runtime and emitted along with the string_constant id. -void read_memory(std::uint32_t addr) { - auto const my_message = format("Reading memory at {:08x}"_sc, addr); -} - -// both runtime and compile time arguments can be used in a single format operation -template -void readReg() { - RegType reg{}; - auto const reg_value = apply(read(reg.raw())); - auto const my_message = format("Register {} = {}"_sc, type_, reg_value); -} -``` - -Note that values known at compile time are passed into template variables as -template parameters. This allows the string.format method to access constexpr -versions of the values and format them into the string. - -If you format integers or enums without using the template variables, then the -formatting happens outside of firmware by the log decoder collateral. - -## Theory of Operation - -Compile-time string literals are created using user-defined string template -literals. These are not yet part of a C++ standard but are supported by GCC, -Clang, and MSVC. They allow for easy conversion of a string literal to template -parameters. - -```c++ -template -constexpr auto operator""_sc() { - return sc::string_constant{}; -} -``` - -We can see clearly that the string value is encoded in the type. - -```c++ -// create a compile-time string. note that it does not need to be constexpr or even const because the value is encoded in the type. -auto hello_value = "Hello!"_sc; -using HelloType = decltype(hello_value); - -// the string's value is represented by its type -using ExpectedType = sc::string_constant; - -ExpectedType expected_value{}; - -// same type -static_assert(std::is_same_v); - -// same value -static_assert(hello_value == expected_value); -``` - -Encoding the string value in the type is necessary for string_constant support. -Type information is used when linking object files together. This means the -string values can be present in object files to be extracted during the build -process. The other part is assigning a unique ID to each string. This can be -done with the declaration of a template function with external linkage like -this: - -```c++ -template -extern std::uint32_t get_string_id(StringType); -``` - -When a string needs to be emitted as part of a log message, the `get_string_id()` -function is used to translate the string type to its ID. During compilation, an -intermediate object file is generated missing a definition for each -instantiation of the `get_string_id()` function. This information can be extracted -using the 'nm' tool and the original string values recovered. A -new `strings.cpp` file can be generated that implements all the template -instantiations returning a unique ID for each string. When the original object -binary is linked to `strings.o`, the calls to `get_string_id()` can be replaced -with the actual int value. - diff --git a/include/sc/format.hpp b/include/sc/format.hpp deleted file mode 100644 index cc659ba7..00000000 --- a/include/sc/format.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace sc { -namespace detail { -template -concept compile_time_field = - std::same_as>; - -template [[nodiscard]] CONSTEVAL auto field_value(T) { - if constexpr (std::is_enum_v) { - return stdx::enum_as_string(); - } else { - return T::value; - } -} - -template -[[nodiscard]] CONSTEVAL auto field_value(sc::type_name) { - return stdx::type_as_string(); -} - -template constexpr auto format1(Fmt, Arg arg) { - constexpr auto str = [&] { - constexpr auto fmtstr = FMT_COMPILE(Fmt::value); - constexpr auto sz = fmt::formatted_size(fmtstr, field_value(arg)); - std::array buf{}; - fmt::format_to(std::begin(buf), fmtstr, field_value(arg)); - return buf; - }(); - return [&](std::index_sequence) { - return string_constant{}; - }(std::make_index_sequence{}); -} - -template constexpr auto split_format_spec() { - constexpr Fmt fmt{}; - constexpr auto spec_start = std::adjacent_find( - std::begin(fmt), std::end(fmt), - [](auto c1, auto c2) { return c1 == '{' and c2 != '{'; }); - if constexpr (spec_start == std::end(fmt)) { - return std::pair{fmt, ""_sc}; - } else { - constexpr auto spec_end = std::find_if(spec_start, std::end(fmt), - [](auto c) { return c == '}'; }); - constexpr auto len = - static_cast(std::distance(std::begin(fmt), spec_end) + 1); - return std::pair{fmt.substr(int_<0>, int_), fmt.substr(int_)}; - } -} - -template -constexpr auto process_arg(stdx::tuple t, Arg arg) { - using namespace stdx::literals; - constexpr auto p = split_format_spec(); - if constexpr (requires { field_value(arg); }) { - return stdx::make_tuple(t[0_idx] + format1(p.first, arg), p.second, - t[2_idx]); - } else if constexpr (requires { arg.args; }) { - return stdx::make_tuple(t[0_idx] + format1(p.first, arg.str), p.second, - stdx::tuple_cat(t[2_idx], arg.args)); - } else { - return stdx::make_tuple( - t[0_idx] + p.first, p.second, - stdx::tuple_cat(t[2_idx], stdx::make_tuple(arg))); - } -} -} // namespace detail - -template -constexpr auto format(Fmt, Args... args) { - using namespace stdx::literals; - auto t = stdx::make_tuple(args...); - auto r = - t.fold_left(stdx::make_tuple(""_sc, Fmt{}, stdx::tuple{}), - [](auto x, auto y) { return detail::process_arg(x, y); }); - if constexpr (r[2_idx].size() == 0) { - return r[0_idx] + r[1_idx]; - } else { - return lazy_string_format{r[0_idx] + r[1_idx], r[2_idx]}; - } -} -} // namespace sc diff --git a/include/sc/fwd.hpp b/include/sc/fwd.hpp deleted file mode 100644 index 5d0b3d6e..00000000 --- a/include/sc/fwd.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace sc { -template -constexpr static std::integral_constant int_{}; - -template -constexpr static std::integral_constant uint_{}; - -template -constexpr static std::integral_constant bool_{}; - -template -constexpr static std::integral_constant char_{}; - -template - requires std::is_enum_v -constexpr static std::integral_constant enum_{}; - -template struct type_name { - constexpr explicit type_name(T) {} - constexpr type_name() = default; -}; - -template constexpr static type_name type_{}; - -template struct string_constant; - -inline namespace literals { -template CONSTEVAL auto operator""_sc() { - return stdx::ct_string_to_type(); -} -} // namespace literals - -template -concept sc_like = requires(T const &t) { - t.apply([](StringType, auto const &...) {}); -}; -} // namespace sc -using sc::literals::operator""_sc; diff --git a/include/sc/lazy_string_format.hpp b/include/sc/lazy_string_format.hpp deleted file mode 100644 index c5428fd2..00000000 --- a/include/sc/lazy_string_format.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -#include - -namespace sc { -template -struct lazy_string_format { - constexpr static StringConstant str{}; - ArgTuple args{}; - - constexpr lazy_string_format() = default; - constexpr lazy_string_format(StringConstant, ArgTuple newArgs) - : args{newArgs} {} - - template constexpr auto apply(F &&f) const { - return args.apply( - [&](auto const &...as) { return std::forward(f)(str, as...); }); - } -}; - -template -[[nodiscard]] constexpr auto operator==( - lazy_string_format, ArgsTupleLhs> lhs, - lazy_string_format, ArgsTupleRhs> - rhs) noexcept -> bool { - return (lhs.str == rhs.str) && (lhs.args == rhs.args); -} - -template -[[nodiscard]] constexpr auto -operator+(lazy_string_format lhs, - lazy_string_format rhs) noexcept { - return lazy_string_format{lhs.str + rhs.str, - stdx::tuple_cat(lhs.args, rhs.args)}; -} - -template -[[nodiscard]] constexpr auto -operator+(lazy_string_format lhs, - string_constant rhs) noexcept { - return lazy_string_format{lhs.str + rhs, lhs.args}; -} - -template -[[nodiscard]] constexpr auto -operator+(string_constant lhs, - lazy_string_format rhs) noexcept { - return lazy_string_format{lhs + rhs.str, rhs.args}; -} -} // namespace sc diff --git a/include/sc/string_constant.hpp b/include/sc/string_constant.hpp deleted file mode 100644 index 17c32729..00000000 --- a/include/sc/string_constant.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include - -namespace sc { -template struct string_constant { - using value_type = std::basic_string_view; - - private: - constexpr static std::array storage{chars...}; - - using size_type = int; - using const_iterator = typename value_type::const_iterator; - constexpr static size_type npos = std::numeric_limits::max(); - - public: - constexpr static value_type value{storage.data(), sizeof...(chars)}; - - constexpr static auto begin() noexcept { return std::cbegin(storage); } - constexpr static auto end() noexcept { return std::cend(storage); } - - [[nodiscard]] constexpr static auto size() noexcept { - return std::size(storage); - } - - template - [[nodiscard]] constexpr static auto - substr(std::integral_constant, - std::integral_constant = {}) { - constexpr size_type sz = count == npos ? size() - pos : count; - return [&](std::integer_sequence) { - return string_constant{}; - }(std::make_integer_sequence{}); - } - - template constexpr auto apply(F &&f) const { - return std::forward(f)(*this); - } -}; - -template -[[nodiscard]] constexpr auto -operator==(string_constant, - string_constant) noexcept -> bool { - return false; -} - -template -[[nodiscard]] constexpr auto -operator==(string_constant, - string_constant) noexcept -> bool { - return true; -} - -#if __cpp_lib_three_way_comparison < 201907L -template -[[nodiscard]] constexpr auto -operator!=(string_constant lhs, - string_constant rhs) noexcept -> bool { - return not(lhs == rhs); -} -template -[[nodiscard]] constexpr auto -operator<(string_constant lhs, - string_constant rhs) noexcept -> bool { - return lhs.value < rhs.value; -} -template -[[nodiscard]] constexpr auto -operator>(string_constant lhs, - string_constant rhs) noexcept -> bool { - return lhs.value > rhs.value; -} -template -[[nodiscard]] constexpr auto -operator<=(string_constant lhs, - string_constant rhs) noexcept -> bool { - return lhs.value <= rhs.value; -} -template -[[nodiscard]] constexpr auto -operator>=(string_constant lhs, - string_constant rhs) noexcept -> bool { - return lhs.value >= rhs.value; -} -#else -template -[[nodiscard]] constexpr auto -operator<=>(string_constant lhs, - string_constant rhs) noexcept { - return lhs.value <=> rhs.value; -} -#endif - -template -[[nodiscard]] constexpr auto -operator+(string_constant, - string_constant) noexcept - -> string_constant { - return {}; -} -} // namespace sc diff --git a/include/seq/step.hpp b/include/seq/step.hpp index 6e682927..ce29cde3 100644 --- a/include/seq/step.hpp +++ b/include/seq/step.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -31,8 +30,7 @@ struct rt_step { template struct ct_step : rt_step { using is_subgraph = void; - using name_t = - decltype(stdx::ct_string_to_type()); + using name_t = stdx::cts_t; constexpr static auto identity = flow::subgraph_identity::VALUE; @@ -56,10 +54,8 @@ struct ct_step : rt_step { */ template [[nodiscard]] constexpr auto step(func_ptr forward, func_ptr backward) { - return ct_step{ - {forward, backward, [] { - CIB_TRACE("seq.step({})", - stdx::ct_string_to_type()); - }}}; + return ct_step{{forward, backward, [] { + CIB_TRACE("seq.step({})", stdx::cts_t{}); + }}}; } } // namespace seq diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d741c2d4..ae5260b9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,5 +23,4 @@ add_subdirectory(log) add_subdirectory(lookup) add_subdirectory(match) add_subdirectory(msg) -add_subdirectory(sc) add_subdirectory(seq) diff --git a/test/sc/CMakeLists.txt b/test/sc/CMakeLists.txt deleted file mode 100644 index aefed6a0..00000000 --- a/test/sc/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_tests(FILES format string_constant LIBRARIES cib_sc) diff --git a/test/sc/format.cpp b/test/sc/format.cpp deleted file mode 100644 index f0381ece..00000000 --- a/test/sc/format.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include - -#include - -#include -#include - -TEST_CASE("format a static string", "[sc::format]") { - static_assert(sc::format("Hello"_sc) == "Hello"_sc); -} - -TEST_CASE("interpolate 1 string", "[sc::format]") { - static_assert(sc::format("Hello, {} is a good day!"_sc, "today"_sc) == - "Hello, today is a good day!"_sc); -} - -TEST_CASE("ignore escaped curly braces", "[sc::format]") { - static_assert(sc::format("Hello, {} {{is}} a good day!"_sc, "today"_sc) == - "Hello, today {{is}} a good day!"_sc); -} - -TEST_CASE("interpolate N strings", "[sc::format]") { - static_assert(sc::format("This {} is a {}."_sc, "box"_sc, "package"_sc) == - "This box is a package."_sc); - static_assert(sc::format("{} {} {} arguments."_sc, "There"_sc, "are"_sc, - "three"_sc) == "There are three arguments."_sc); -} - -TEST_CASE("interpolate empty string", "[sc::format]") { - static_assert(sc::format("[{}]"_sc, ""_sc) == "[]"_sc); -} - -namespace { -template -struct my_integral_constant : public std::integral_constant {}; -} // namespace - -TEST_CASE("interpolate integral_constant", "[sc::format]") { - static_assert(sc::format("The answer is {}."_sc, sc::int_<42>) == - "The answer is 42."_sc); - static_assert(format("{}"_sc, my_integral_constant{}) == "42"_sc); -} - -TEST_CASE("interpolate mixed types", "[sc::format]") { - static_assert(sc::format("Only {} more days until {}."_sc, sc::int_<100>, - "retirement"_sc) == - "Only 100 more days until retirement."_sc); -} - -namespace { -enum class cmd { READ, WRITE }; -} - -TEST_CASE("interpolate enum", "[sc::format]") { - static_assert(sc::format("Command = {}"_sc, sc::enum_) == - "Command = WRITE"_sc); - static_assert(sc::format("Command = {}"_sc, sc::enum_) == - "Command = READ"_sc); -} - -struct complete {}; -struct incomplete; - -TEST_CASE("interpolate typenames", "[sc::format]") { - static_assert(sc::format("Type = {}"_sc, sc::type_) == - "Type = complete"_sc); - static_assert(sc::format("Type = {}"_sc, sc::type_) == - "Type = incomplete"_sc); -} - -TEST_CASE("int formatting options", "[sc::format]") { - static_assert(sc::format("{:d}"_sc, sc::int_<42>) == "42"_sc); - static_assert(sc::format("{:b}"_sc, sc::int_<17>) == "10001"_sc); - static_assert(sc::format("{:x}"_sc, sc::int_<0xba115>) == "ba115"_sc); - static_assert(sc::format("{:X}"_sc, sc::int_<0xba115>) == "BA115"_sc); - static_assert(sc::format("{:o}"_sc, sc::int_<16>) == "20"_sc); - static_assert(sc::format("{:08x}"_sc, sc::int_<0xbea75>) == "000bea75"_sc); - static_assert(sc::format("{:8x}"_sc, sc::int_<0xbea75>) == " bea75"_sc); - static_assert(sc::format("{:4x}"_sc, sc::int_<0xbea75>) == "bea75"_sc); - static_assert(sc::format("{:04x}"_sc, sc::int_<0xbea75>) == "bea75"_sc); -} - -TEST_CASE("runtime integral values", "[sc::format]") { - static_assert(sc::format("{}"_sc, 0) == - (sc::lazy_string_format{"{}"_sc, stdx::make_tuple(0)})); - static_assert(sc::format("{}"_sc, 1) == - (sc::lazy_string_format{"{}"_sc, stdx::make_tuple(1)})); - static_assert(sc::format("I am {} and my sister is {}"_sc, 6, 8) == - (sc::lazy_string_format{"I am {} and my sister is {}"_sc, - stdx::make_tuple(6, 8)})); - static_assert(sc::format("{}"_sc, 100) != - (sc::lazy_string_format{"{}"_sc, stdx::make_tuple(99)})); - static_assert(sc::format("{}"_sc, true) == - (sc::lazy_string_format{"{}"_sc, stdx::make_tuple(true)})); -} - -TEST_CASE("mixed runtime and compile time values", "[sc::format]") { - static_assert( - sc::format("ct value {} and rt value {} mixed"_sc, "ctval"_sc, 1) == - (sc::lazy_string_format{"ct value ctval and rt value {} mixed"_sc, - stdx::make_tuple(1)})); - static_assert( - sc::format("rt value {} and ct value {} mixed"_sc, 1, "ctval"_sc) == - (sc::lazy_string_format{"rt value {} and ct value ctval mixed"_sc, - stdx::make_tuple(1)})); -} - -TEST_CASE("format a formatted string", "[sc::format]") { - static_assert(sc::format("Hello {}!"_sc, sc::format("World"_sc)) == - "Hello World!"_sc); - - static_assert( - sc::format("The value is {}."_sc, sc::format("(year={})"_sc, 2022)) == - (sc::lazy_string_format{"The value is (year={})."_sc, - stdx::make_tuple(2022)})); - - static_assert(sc::format("a{}b{}c"_sc, sc::format("1{}2{}3"_sc, 10, 20), - sc::format("4{}5{}6"_sc, 30, 40)) == - (sc::lazy_string_format{"a1{}2{}3b4{}5{}6c"_sc, - stdx::make_tuple(10, 20, 30, 40)})); -} - -TEST_CASE("lazy_string_format is sc_like", "[sc::format]") { - [[maybe_unused]] auto lsf = - sc::lazy_string_format{"{}"_sc, stdx::make_tuple(0)}; - static_assert(sc::sc_like); -} diff --git a/test/sc/string_constant.cpp b/test/sc/string_constant.cpp deleted file mode 100644 index 24555d76..00000000 --- a/test/sc/string_constant.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include - -#include - -TEST_CASE("creation", "[sc::string_constant]") { - [[maybe_unused]] auto hi = "Hello, cruel world!"_sc; - [[maybe_unused]] auto empty = ""_sc; -} - -TEST_CASE("substr", "[sc::string_constant]") { - auto hi = "Hello, cruel world!"_sc; - auto sub = hi.substr(sc::int_<0>, sc::int_<5>); - static_assert(sub == "Hello"_sc); -} - -TEST_CASE("equality", "[sc::string_constant]") { - static_assert("hi"_sc == "hi"_sc); - static_assert(!("hi"_sc == "hello"_sc)); - - static_assert("hi"_sc != "hello"_sc); - static_assert(!("hello"_sc != "hello"_sc)); -} - -TEST_CASE("comparison", "[sc::string_constant]") { - static_assert("abc"_sc < "abd"_sc); - static_assert("abcd"_sc > "abc"_sc); - static_assert("abc"_sc <= "abd"_sc); - static_assert("abcd"_sc >= "abc"_sc); -} - -TEST_CASE("join", "[sc::string_constant]") { - static_assert("Hello, "_sc + "cruel world!"_sc == "Hello, cruel world!"_sc); - static_assert(""_sc + "Luke"_sc == "Luke"_sc); - static_assert("Computer"_sc + ""_sc == "Computer"_sc); -} - -TEST_CASE("string_constant is sc_like", "[sc::string_constant]") { - [[maybe_unused]] auto hi = "Hello, cruel world!"_sc; - static_assert(sc::sc_like); -}