/* ****************************************************************************
* 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