/* **************************************************************************** * Copyright 2019 Open Systems Development BV * * * * 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. * * ***************************************************************************/ #ifndef OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H #define OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H /// @file /// /// Support for compiletime string building. /// /// In some cases it is necessary to embed string messages as read only string literals /// so that they can be used in low memory conditions for example. /// This header defines two macro's that can be used to build managed string literals. /// These managed string literals can be concatenated and searched at compiletime. /// When the compiler is done with this code only the embedded string literal that are /// used by normal expressions remain in the binary. /// The implementation is a mix of meta template programming and the use of constexpr /// expressions and is based on an idea found on stackoverflow. /// This code is meant to handle small strings (up to say 100 characters). Larger strings can trigger the maximum /// template recursion depth of the compiler (-ftemplate-depth). This depth is set at 900. So in principle strings /// of 900 characters can be handled. However this is very inefficient and will prolong compiletime severly. /// /// Here follows an example of how this code can be used. /// @code /// auto str1 = OSDEV_COMPONENTS_CSTRING("This is a test"); /// auto str2 = OSDEV_COMPONENTS_CSTRING(" with concatenation"); /// auto str3 = str1 + str2; /// const char* s = str3.chars; /// @endcode /// str3.chars contains the compiletime concatenated string. /// /// When examing the binary one will see that it contains the null terminated string literal /// @code /// This is a test with concatenation^@ /// @endcode /// The str1 and str2 literals are discarded since they are never used in a non compiletime way. /// @note This code is not meant to handle string literals with internal \0 characters. Beware! #include #include "metaprogrammingdefs.h" /// @brief Build a managed substring string literal from a string literal input /// The strings are build compile time. /// The upper bound is one past the last character that the caller is interested in. #define OSDEV_COMPONENTS_CSTRING_BOUNDED(string_literal, lower, upper) \ [] { \ constexpr std::size_t lb = (lower); \ constexpr std::size_t ub = (upper); \ static_assert(lb <= ub, "lower bound must be smaller than or equal to upper bound"); \ static_assert(ub < sizeof(string_literal), \ "upper bound must be smaller than or equal to the length of the string"); \ struct constexpr_string_type \ { \ const char* chars = string_literal; \ }; \ return typename osdev::components::mqtt::apply_bounded_range::template produce>::result{}; \ }() /// @brief Build a managed string literal from a string literal input #define OSDEV_COMPONENTS_CSTRING(string_literal) \ OSDEV_COMPONENTS_CSTRING_BOUNDED(string_literal, 0, sizeof(string_literal) - 1) namespace osdev { namespace components { namespace mqtt { /// @brief Managed string literal. /// This class is used to hold string literals at compile time. template struct compiletime_string { /// @brief Declaration of the string static constexpr const char chars[sizeof...(str) + 1] = { str..., '\0' }; }; /// @brief Definition of the string template constexpr const char compiletime_string::chars[sizeof...(str) + 1]; /// @brief Managed string literal builder. /// This class is used to build string literals at compile time. template struct string_builder { /// @brief maps indices list on the char array template struct produce { typedef compiletime_string result; }; }; /// @brief Adapter for coupling other producers to string_builder. /// tparam T The type of producer to adapt. /// @note The adapter must define its return type as return_t and provide a static method produce() which produces the result. /// The return type must be a kind of array type of chars (char[len], std::array, etc). template struct adapt_for_string_builder { static constexpr typename T::return_t chars = T::produce(); }; /// @brief Concatenate two managed string literals /// The literals are packed in a variadic template. /// These templates are formed by the MLOGIC_COMMON_CSTRING* macro's /// The concatenation is done at compiletime. template compiletime_string operator+(compiletime_string, compiletime_string) { return {}; } /// @brief Search for a character in a string literal from the right side. /// The character index is returned relative to the left side. /// If the character is not found a std::size_t(-1) is returned. /// The default search starts at the end of the string. The index /// can be given to start the search at another point in the string. /// The index is given as nr of characters from the right end side of /// the string. To proceed with a search see following example. /// @code /// constexpr const char s[] = "This is a test"; /// constexpr std::size_t index = osdev_components::mqtt::rfind(s, 'i'); // gives 5 /// static_assert(index == 5, ""); /// constexpr std::size_t index2 = osdev::components::mqtt::rfind(s, 'i', sizeof(s) - index); // gives 2 /// static_assert(index2 == 2, ""); /// @endcode /// This function should only be used at compile time! template constexpr std::size_t rfind(const char (&str)[N], char c, std::size_t index = 0) { return index >= N ? std::numeric_limits::max() : str[N - index - 1] == c ? N - index - 1 : rfind(str, c, index + 1); } } // End namespace mqtt } // End namespace components } // End namespace osdev #endif // OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H