From 75799f46529124848862e5e42335ac422fa57dfe Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 12 Apr 2024 20:45:04 -0400 Subject: [PATCH] initial commit --- CMakeLists.txt | 57 +++++++++++ cmake/xo-bootstrap-macros.cmake | 26 +++++ cmake/xo_stringliteralConfig.cmake.in | 17 ++++ example/CMakeLists.txt | 1 + example/ex1/CMakeLists.txt | 15 +++ example/ex1/ex1.cpp | 38 ++++++++ .../xo/stringliteral/string_view_concat.hpp | 30 ++++++ include/xo/stringliteral/stringliteral.hpp | 96 +++++++++++++++++++ .../stringliteral/stringliteral_iostream.hpp | 36 +++++++ 9 files changed, 316 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/xo-bootstrap-macros.cmake create mode 100644 cmake/xo_stringliteralConfig.cmake.in create mode 100644 example/CMakeLists.txt create mode 100644 example/ex1/CMakeLists.txt create mode 100644 example/ex1/ex1.cpp create mode 100644 include/xo/stringliteral/string_view_concat.hpp create mode 100644 include/xo/stringliteral/stringliteral.hpp create mode 100644 include/xo/stringliteral/stringliteral_iostream.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b7b5d58 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,57 @@ +# xo-stringliteral/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_stringliteral VERSION 1.0) +enable_language(CXX) + +# common XO cmake macros (see proj/xo-cmake) +include(cmake/xo-bootstrap-macros.cmake) + +# ---------------------------------------------------------------- +# unit test setup + +enable_testing() +# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) +add_code_coverage() + +# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. +# we're not interested in code coverage for these sources. +# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves; +# rather, want coverage on the code that the unit tests exercise. +# +# NOTE: this seems to work only with the 'ccov-all' target. In particular, doesn't seem to do anything with the 'ccov' target +# +add_code_coverage_all_targets(EXCLUDE /nix/store/* ${PROJECT_SOURCE_DIR}/utest/* ${PROJECT_BINARY_DIR}/local/* ${PROJECT_SOURCE_DIR}/repo/*) + +# ---------------------------------------------------------------- +# c++ settings + +# one-time project-specific c++ flags. usually empty +set(PROJECT_CXX_FLAGS "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") +add_definitions(${PROJECT_CXX_FLAGS}) + +xo_toplevel_compile_options() + +# ---------------------------------------------------------------- + +add_subdirectory(example) +#add_subdirectory(utest) +#add_subdirectory(docs) + +# ---------------------------------------------------------------- +# provide find_package() support for projects using this library + +set(SELF_LIB xo_stringliteral) +xo_add_headeronly_library(${SELF_LIB}) +xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets) +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# ---------------------------------------------------------------- +# dependencies + +#xo_headeronly_dependency(${SELF_LIB} randomgen) +# etc.. + +# end CMakeLists.txt diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 0000000..0b6a916 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,26 @@ +# ---------------------------------------------------------------- +# for example use +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will set +# CMAKE_MODULE_PATH = /usr/local/share/cmake +# and expect .cmake macros in +# /usr/local/share/cmake/xo_macros/xo-project-macros.cmake +# ---------------------------------------------------------------- + +if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +endif() + +if (NOT XO_SUBMODULE_BUILD) + message("-- GUESSED_CMAKE_CMD=cmake -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -B ${CMAKE_BINARY_DIR}") + message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") + message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo-project-macros) diff --git a/cmake/xo_stringliteralConfig.cmake.in b/cmake/xo_stringliteralConfig.cmake.in new file mode 100644 index 0000000..e5ee177 --- /dev/null +++ b/cmake/xo_stringliteralConfig.cmake.in @@ -0,0 +1,17 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +# note: changes to find_dependency() calls here +# must coordinate with xo_dependency() calls +# in xo-reactor/src/reactor/CMakeLists.txt +# +#find_dependency(reflect) +#find_dependency(subsys) +#find_dependency(Eigen3) +#find_dependency(webutil) +#find_dependency(printjson) +#find_dependency(callback) + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..4151ec2 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(ex1) diff --git a/example/ex1/CMakeLists.txt b/example/ex1/CMakeLists.txt new file mode 100644 index 0000000..abe3497 --- /dev/null +++ b/example/ex1/CMakeLists.txt @@ -0,0 +1,15 @@ +# xo-stringliteral/example/ex1/CMakeLists.txt + +set(SELF_EXE xo_stringliteral_ex1) +set(SELF_SRCS ex1.cpp) + +add_executable(${SELF_EXE} ${SELF_SRCS}) +xo_include_options2(${SELF_EXE}) + +# ---------------------------------------------------------------- +# dependencies.. + +xo_self_dependency(${SELF_EXE} xo_stringliteral) +#xo_dependency(${SELF_EXE} reflect) + +# end CMakeLists.txt diff --git a/example/ex1/ex1.cpp b/example/ex1/ex1.cpp new file mode 100644 index 0000000..dcb2a45 --- /dev/null +++ b/example/ex1/ex1.cpp @@ -0,0 +1,38 @@ +/* @file ex1.cpp */ + +#include "xo/stringliteral/stringliteral.hpp" +#include "xo/stringliteral/stringliteral_iostream.hpp" +#include "xo/stringliteral/string_view_concat.hpp" +#include + +int +main() { + using namespace std; + using xo::stringliteral; + using xo::stringliteral_compare; + +#ifdef NOT_USING + constexpr stringliteral s1("hello"); + + static_assert(stringliteral_compare(s1, s1) == 0); + + cerr << s1 << endl; + + constexpr stringliteral s2 = stringliteral_concat(stringliteral("hello"), + stringliteral(", world")); + +#endif + + static constexpr string_view hello("hello"); + static constexpr string_view world(" world"); + + static constexpr auto s2 = concat_v; + + static constexpr string_view hello_world("hello world"); + + static_assert(s2 == hello_world); + + cerr << hello_world << endl; +} + +/* end ex1.cpp */ diff --git a/include/xo/stringliteral/string_view_concat.hpp b/include/xo/stringliteral/string_view_concat.hpp new file mode 100644 index 0000000..4105420 --- /dev/null +++ b/include/xo/stringliteral/string_view_concat.hpp @@ -0,0 +1,30 @@ +#include +#include + +template +struct sv_concat +{ + static constexpr auto impl() noexcept { + constexpr std::size_t n = (Strings.size() + ... + 0); + + std::array arr{}; + + auto append = [i=0, &arr](const auto & s) mutable { + for (auto c : s) + arr[i++] = c; + }; + (append(Strings), ...); + arr[n] = '\0'; + + return arr; + } + + static constexpr auto arr = impl(); + static constexpr std::string_view value { + arr.data(), + arr.size() - 1 + }; +}; + +template +static constexpr auto concat_v = sv_concat::value; diff --git a/include/xo/stringliteral/stringliteral.hpp b/include/xo/stringliteral/stringliteral.hpp new file mode 100644 index 0000000..7223572 --- /dev/null +++ b/include/xo/stringliteral/stringliteral.hpp @@ -0,0 +1,96 @@ +/** @file stringliteral.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include +#include +#include + +namespace xo { + /** @class stringliteral + * + * @brief class to represent a literal string at compile time, for use as template argument + **/ + template + struct stringliteral { + constexpr stringliteral() { if (N > 0) value_[0] = '\0'; } + constexpr stringliteral(const char (&str)[N]) { std::copy_n(str, N, value_); } + constexpr int size() const { return N; } + + constexpr char const * c_str() const { return value_; } + + char value_[N]; + }; + + /** @brief all_same_v is true iff types T1 = .. = Tn + **/ + template < typename First, typename... Rest > + constexpr auto + all_same_v = std::conjunction_v< std::is_same... >; + + /** @brief concatenate string literals + * + * NOTE: this isn't constexpr in clang16 + **/ + template < typename... Ts> + constexpr auto + stringliteral_concat(Ts && ... args) + { +#ifdef NOT_USING + static_assert(all_same_v...>, + "string must share the same char type"); + + using char_type = std::remove_const_t< std::remove_pointer_t < std::common_type_t < Ts... > > >; +#endif + using char_type = char; + + /** n1: total number of bytes used by arguments **/ + constexpr size_t n1 = (sizeof(Ts) + ...); + /** z1: each string arg has a null terminator included in its size, + * z1 is the number of arguments in parameter pack Ts, + * which equals the number of null terminators used + **/ + constexpr size_t z1 = sizeof...(Ts); + + /** n: number of chars in concatenated string. +1 for final null **/ + constexpr size_t n + = (n1 / sizeof(char_type)) - z1 + 1; + + stringliteral result; + size_t pos = 0; + + auto detail_concat = [ &pos, &result ](auto && arg) { + constexpr auto count = (sizeof(arg) - sizeof(char_type)) / sizeof(char_type); + + std::copy_n(arg.c_str(), count, result.value_ + pos); + pos += count; + }; + + (detail_concat(args), ...); + + //return stringliteral(""); + return result; + } + +#ifdef NOT_USING + template + constexpr auto + stringliteral_compare(stringliteral && s1, stringliteral && s2) + { + return std::string_view(s1.value_) <=> std::string_view(s2.value_); + } +#endif + + template + constexpr auto + stringliteral_compare(const stringliteral & s1, const stringliteral & s2) + { + return std::string_view(s1.value_) <=> std::string_view(s2.value_); + } +} /*namespace xo*/ + + +/** end stringliteral.hpp **/ diff --git a/include/xo/stringliteral/stringliteral_iostream.hpp b/include/xo/stringliteral/stringliteral_iostream.hpp new file mode 100644 index 0000000..18a371a --- /dev/null +++ b/include/xo/stringliteral/stringliteral_iostream.hpp @@ -0,0 +1,36 @@ +/** @file stringliteral_iostream.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "stringliteral.hpp" +#include +//#include + +namespace xo { + /** @brief print stringliteral on stream os. + * + **/ + template + void + print_stringliteral (std::ostream & os, const stringliteral & x) { + os << x.c_str(); + } + + /** @brief print stringliteral x on stream os. + * + * Example + * @code + * cout << stringliteral("foo"); // outputs "foo" + **/ + template + inline std::ostream & + operator<< (std::ostream & os, const stringliteral & x) { + print_stringliteral(os, x); + return os; + } +} /*namespace xo*/ + +/** end stringliteral_iostream.hpp **/