Add conveniently usable string builder

which allows fast string building without multiple heap allocations
This commit is contained in:
Martchus 2017-01-26 22:15:27 +01:00
parent f23c381da4
commit a772cdf30b
5 changed files with 206 additions and 1 deletions

View File

@ -16,6 +16,7 @@ set(HEADER_FILES
conversion/binaryconversionprivate.h
conversion/conversionexception.h
conversion/stringconversion.h
conversion/stringbuilder.h
conversion/types.h
conversion/widen.h
io/ansiescapecodes.h

View File

@ -11,6 +11,7 @@ The library utilizes:
- split, join, find and replace
- conversion from number to string and vice verca
- encoding/decoding base-64
- building string without multiple heap allocations (string builder)
* IO
- reading/writing primitive data types of various sizes (little-endian and big-endian)
- reading/writing terminated strings and size-prefixed strings

184
conversion/stringbuilder.h Normal file
View File

@ -0,0 +1,184 @@
#ifndef CONVERSION_UTILITIES_STRINGBUILDER_H
#define CONVERSION_UTILITIES_STRINGBUILDER_H
#include "../misc/traits.h"
#include <string>
#include <tuple>
namespace ConversionUtilities
{
/// \cond
namespace Helper {
template<class StringType, Traits::DisableIf<std::is_integral<StringType> > >
std::size_t computeTupleElementSize(const StringType *str)
{
return str->size();
}
template<class StringType>
std::size_t computeTupleElementSize(const StringType &str)
{
return str.size();
}
template<class CharType>
std::size_t computeTupleElementSize(const CharType *str)
{
return std::char_traits<CharType>::length(str);
}
template<class StringType>
void append(StringType &target, const StringType *str)
{
target.append(*str);
}
template<class StringType>
void append(StringType &target, const StringType &str)
{
target.append(str);
}
template<class StringType, class CharType>
void append(StringType &target, const CharType *str)
{
target.append(str);
}
template<class StringType, class Tuple, std::size_t N>
struct TupleToString {
static std::size_t precomputeSize(const Tuple &tuple)
{
return TupleToString<StringType, Tuple, N-1>::precomputeSize(tuple) + computeTupleElementSize(std::get<N-1>(tuple));
}
static void append(const Tuple &tuple, StringType &str)
{
TupleToString<StringType, Tuple, N-1>::append(tuple, str);
Helper::append(str, std::get<N-1>(tuple));
}
};
template<class StringType, class Tuple>
struct TupleToString<StringType, Tuple, 1> {
static std::size_t precomputeSize(const Tuple &tuple)
{
return computeTupleElementSize(std::get<0>(tuple));
}
static void append(const Tuple &tuple, StringType &str)
{
Helper::append(str, std::get<0>(tuple));
}
};
template<class... Elements>
class StringTuple : public std::tuple<Elements...>
{
public:
StringTuple(Elements&&... elements) :
std::tuple<Elements...>(elements...)
{}
};
template<class... Elements>
constexpr auto makeStringTuple(Elements&&... elements) -> decltype(StringTuple<Elements...>(elements...))
{
return StringTuple<Elements...>(elements...);
}
}
/// \endcond
/*!
* \brief Concatenates all strings hold by the specified \a tuple.
*/
template<class StringType = std::string, class... Args>
StringType tupleToString(const std::tuple<Args...> &tuple)
{
StringType res;
res.reserve(Helper::TupleToString<StringType, decltype(tuple), sizeof...(Args)>::precomputeSize(tuple));
Helper::TupleToString<StringType, decltype(tuple), sizeof...(Args)>::append(tuple, res);
return res;
}
/*!
* \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3.
*/
template<class Tuple>
constexpr auto operator %(const Tuple &lhs, const std::string &rhs) -> decltype(std::tuple_cat(lhs, std::make_tuple(&rhs)))
{
return std::tuple_cat(lhs, std::make_tuple(&rhs));
}
/*!
* \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3.
*/
template<class Tuple>
constexpr auto operator %(const Tuple &lhs, const char *rhs) -> decltype(std::tuple_cat(lhs, std::make_tuple(rhs)))
{
return std::tuple_cat(lhs, std::make_tuple(rhs));
}
/*!
* \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3.
*/
constexpr auto operator %(const std::string &lhs, const std::string &rhs) -> decltype(std::make_tuple(&lhs, &rhs))
{
return std::make_tuple(&lhs, &rhs);
}
/*!
* \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3.
*/
constexpr auto operator %(const char *lhs, const std::string &rhs) -> decltype(std::make_tuple(lhs, &rhs))
{
return std::make_tuple(lhs, &rhs);
}
/*!
* \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3.
*/
constexpr auto operator %(const std::string &lhs, const char *rhs) -> decltype(std::make_tuple(&lhs, rhs))
{
return std::make_tuple(&lhs, rhs);
}
/*!
* \brief Allows construction of final string from previously constructed string-tuple and trailing string via +-operator.
*
* This is meant to be used for fast string building without multiple heap allocation, eg.
*
* ```
* printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)"));
* ```
*/
template<class Tuple>
inline std::string operator +(const Tuple &lhs, const std::string &rhs)
{
return tupleToString(std::tuple_cat(lhs, std::make_tuple(&rhs)));
}
/*!
* \brief Allows construction of final string from previously constructed string-tuple and trailing string via +-operator.
*
* This is meant to be used for fast string building without multiple heap allocation, eg.
*
* ```
* printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)"));
* ```
*/
template<class Tuple>
inline std::string operator +(const Tuple &lhs, const char *rhs)
{
return tupleToString(std::tuple_cat(lhs, std::make_tuple(rhs)));
}
}
#endif // CONVERSION_UTILITIES_STRINGBUILDER_H

View File

@ -1,5 +1,6 @@
#include "../conversion/binaryconversion.h"
#include "../conversion/stringconversion.h"
#include "../conversion/stringbuilder.h"
#include "../tests/testutils.h"
#include <cppunit/extensions/HelperMacros.h>
@ -27,6 +28,7 @@ class ConversionTests : public TestFixture
CPPUNIT_TEST(testSwapOrderFunctions);
CPPUNIT_TEST(testStringEncodingConversions);
CPPUNIT_TEST(testStringConversions);
CPPUNIT_TEST(testStringBuilder);
CPPUNIT_TEST_SUITE_END();
public:
@ -40,6 +42,7 @@ public:
void testSwapOrderFunctions();
void testStringEncodingConversions();
void testStringConversions();
void testStringBuilder();
private:
template<typename intType>
@ -295,3 +298,18 @@ void ConversionTests::testStringConversions()
CPPUNIT_ASSERT(decodedBase64Data.first[i] == originalBase64Data[i]);
}
}
string functionTakingString(const string &str)
{
return str;
}
void ConversionTests::testStringBuilder()
{
const tuple<const char *, string, const char *> tuple("string1", "string2", "string3");
CPPUNIT_ASSERT_EQUAL(string("string1string2string3"), tupleToString(tuple));
CPPUNIT_ASSERT_EQUAL(string("foobarfoo2bar2"), tupleToString(string("foo") % "bar" % string("foo2") % "bar2"));
CPPUNIT_ASSERT_EQUAL(string("123456"), functionTakingString("12" % string("34") + "56"));
constexpr double velocityExample = 27.0;
CPPUNIT_ASSERT_EQUAL(string("velocity: 27 km/h (7.5 m/s)"), functionTakingString("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)"));
}

View File

@ -148,7 +148,8 @@ template <typename T> AsHexNumber<T> asHexNumber(const T &value)
* \brief Asserts successful execution of application via TestApplication::execApp(). Output is stored in stdout and stderr.
* \remarks Requires cppunit.
*/
# define TESTUTILS_ASSERT_EXEC(args) CPPUNIT_ASSERT_EQUAL(0, execApp(args, stdout, stderr))
# define TESTUTILS_ASSERT_EXEC(args) \
CPPUNIT_ASSERT_EQUAL(0, execApp(args, stdout, stderr))
#endif
}