Make trivstring class ready for use

The interface is now 100% unit tested, and the typedefs depend on the new
STD_STRING_USES_COW configuration variable. The only missing bit is to detect
clang and disable STD_STRING_USES_COW for clang.
This commit is contained in:
Georg Baum 2014-12-07 13:12:26 +01:00
parent 46f7b578b2
commit 83bee109db
9 changed files with 281 additions and 22 deletions

View File

@ -259,10 +259,13 @@ if(UNIX OR MINGW)
endif()
set(LYX_GCC11_MODE "${CXX11_FLAG}")
endif()
set(STD_STRING_USES_COW 1)
else()
if(MSVC10)
set(LYX_USE_TR1 1)
#set(LYX_USE_TR1_REGEX 1) #TODO should we use it in ECMAScript mode?
else()
set(STD_STRING_USES_COW 1)
endif()
endif()

View File

@ -297,6 +297,7 @@ if test x$GXX = xyes; then
;;
esac
fi
AC_DEFINE(STD_STRING_USES_COW, 1, [std::string uses copy-on-write])
fi
test "$lyx_pch_comp" = yes && lyx_flags="$lyx_flags pch"
AM_CONDITIONAL(LYX_BUILD_PCH, test "$lyx_pch_comp" = yes)

View File

@ -101,6 +101,8 @@ liblyxsupport_a_SOURCES = \
Translator.h \
Timeout.cpp \
Timeout.h \
trivstring.cpp \
trivstring.h \
types.h \
userinfo.cpp \
userinfo.h \
@ -143,18 +145,21 @@ EXTRA_DIST += \
tests/test_lstrings \
tests/regfiles/convert \
tests/regfiles/filetools \
tests/regfiles/lstrings
tests/regfiles/lstrings \
tests/regfiles/trivstring
TESTS = \
tests/test_convert \
tests/test_filetools \
tests/test_lstrings
tests/test_lstrings \
tests/test_trivstring
check_PROGRAMS = \
check_convert \
check_filetools \
check_lstrings
check_lstrings \
check_trivstring
if INSTALL_MACOSX
ADD_FRAMEWORKS = -framework QtGui -framework QtCore -framework AppKit -framework ApplicationServices
@ -181,6 +186,13 @@ check_lstrings_SOURCES = \
tests/dummy_functions.cpp \
tests/boost.cpp
check_trivstring_LDADD = liblyxsupport.a $(LIBICONV) $(BOOST_LIBS) $(QT_CORE_LIBS) $(LIBSHLWAPI) @LIBS@
check_trivstring_LDFLAGS = $(QT_CORE_LDFLAGS) $(ADD_FRAMEWORKS)
check_trivstring_SOURCES = \
tests/check_trivstring.cpp \
tests/dummy_functions.cpp \
tests/boost.cpp
makeregfiles: ${check_PROGRAMS}
for all in ${check_PROGRAMS} ; do \
./$$all > ${srcdir}/tests/regfiles/$$all ; \

View File

@ -95,6 +95,15 @@ std::string const & empty_string();
// defined in docstring.cpp
bool operator==(docstring const &, char const *);
#ifdef STD_STRING_USES_COW
template<typename Char> class trivial_string;
typedef trivial_string<char> trivstring;
typedef trivial_string<char_type> trivdocstring;
#else
typedef std::string trivstring;
typedef docstring trivdocstring;
#endif
} // namespace lyx
#endif

View File

@ -28,7 +28,7 @@ include_directories(
${ZLIB_INCLUDE_DIR})
set(check_PROGRAMS check_convert check_filetools check_lstrings)
set(check_PROGRAMS check_convert check_filetools check_lstrings check_trivstring)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/regfiles")

View File

@ -1,6 +1,7 @@
#include <config.h>
#include "../trivstring.h"
#include "../docstring.h"
#include <iostream>
@ -27,22 +28,111 @@ void test_trivstring()
// assignment from trivstring
trivstring const c = a;
// assignment from std::string
trivstring const d = input[i];
trivstring d = input[i];
// assignment from trivstring
string const e = a.str();
string const e = a;
// assignment from trivstring via C string
string const f = a.c_str();
if (a.empty())
cout << "empty ";
else
cout << "not empty ";
cout << a.length() << endl;
cout << a.str() << endl;
cout << b.str() << endl;
cout << c.str() << endl;
cout << d.str() << endl;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << d << endl;
cout << e << endl;
cout << f << endl;
// swap
trivstring g("swap");
cout << g << endl;
d.swap(g);
cout << d << endl;
cout << g << endl;
}
// comparison
trivstring const a;
trivstring const b("a");
trivstring const c("b");
trivstring const d("42");
cout << (a == a) << ' ' << (a < a) << endl; // equal strings
cout << (a == b) << ' ' << (a < b) << endl; // different strings, same length
cout << (b == a) << ' ' << (b < a) << endl; // different strings, same length
cout << (a == c) << ' ' << (a < c) << endl; // different strings, different length
cout << (c == a) << ' ' << (c < a) << endl; // different strings, different length
char const * e = "";
char const * f = "b";
char const * g = "42";
cout << (a == e) << ' ' << (e == a) << endl; // empty strings
cout << (c == a) << ' ' << (a == c) << endl; // equal strings
cout << (a == f) << ' ' << (f == a) << endl; // different strings, same length
cout << (a == g) << ' ' << (g == a) << endl; // different strings, different length
}
void test_trivdocstring()
{
docstring const input[] = {
from_ascii(""),
from_ascii("a"),
from_ascii("42"),
from_ascii("max"), // max. string with sso on 64 bit
from_ascii("something which does not fit into sso")
};
size_t const n = sizeof(input) / sizeof(input[0]);
for (size_t i = 0; i < n; ++i) {
// construction from std::string
trivdocstring const a(input[i]);
// construction from trivstring
trivdocstring const b(a);
// assignment from trivstring
trivdocstring const c = a;
// assignment from std::string
trivdocstring d = input[i];
// assignment from trivstring
docstring const e = a;
// assignment from trivstring via C string
docstring const f = a.c_str();
if (a.empty())
cout << "empty ";
else
cout << "not empty ";
cout << a.length() << endl;
cout << to_ascii(a) << endl;
cout << to_ascii(b) << endl;
cout << to_ascii(c) << endl;
cout << to_ascii(d) << endl;
cout << to_ascii(e) << endl;
cout << to_ascii(f) << endl;
// swap
trivdocstring g(from_ascii("swap"));
cout << to_ascii(g) << endl;
d.swap(g);
cout << to_ascii(d) << endl;
cout << to_ascii(g) << endl;
}
// comparison
trivdocstring const a;
trivdocstring const b(from_ascii("a"));
trivdocstring const c(from_ascii("b"));
trivdocstring const d(from_ascii("42"));
cout << (a == a) << ' ' << (a < a) << endl; // equal strings
cout << (a == b) << ' ' << (a < b) << endl; // different strings, same length
cout << (b == a) << ' ' << (b < a) << endl; // different strings, same length
cout << (a == c) << ' ' << (a < c) << endl; // different strings, different length
cout << (c == a) << ' ' << (c < a) << endl; // different strings, different length
// per character initialization works also if char_type != wchar
char_type const e[1] = {'\0'};
char_type const f[2] = {'b', '\0'};
char_type const g[3] = {'4', '2', '\0'};
cout << (a == e) << ' ' << (e == a) << endl; // empty strings
cout << (c == a) << ' ' << (a == c) << endl; // equal strings
cout << (a == f) << ' ' << (f == a) << endl; // different strings, same length
cout << (a == g) << ' ' << (g == a) << endl; // different strings, different length
}
int main()
{
test_trivstring();
test_trivdocstring();
}

View File

@ -1,35 +1,118 @@
0
empty 0
1
swap
swap
not empty 1
a
a
a
a
a
a
2
swap
swap
a
not empty 2
42
42
42
42
42
42
7
swap
swap
42
not empty 7
max sso
max sso
max sso
max sso
max sso
max sso
37
swap
swap
max sso
not empty 37
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
swap
swap
something which does not fit into sso
1 0
0 1
0 0
0 1
0 0
1 1
0 0
0 0
0 0
empty 0
swap
swap
not empty 1
a
a
a
a
a
a
swap
swap
a
not empty 2
42
42
42
42
42
42
swap
swap
42
not empty 3
max
max
max
max
max
max
swap
swap
max
not empty 37
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
something which does not fit into sso
swap
swap
something which does not fit into sso
1 0
0 1
0 0
0 1
0 0
1 1
0 0
0 0
0 0

View File

@ -13,6 +13,7 @@
#include "support/trivstring.h"
#include "support/docstring.h"
#ifdef STD_STRING_USES_COW
#include <algorithm>
using namespace std;
@ -136,11 +137,10 @@ int trivial_string<Char>::compare(trivial_string const & other) const
}
template string trivial_string<char>::str() const;
template docstring trivial_string<char_type>::str() const;
template trivial_string<char>::operator string() const;
template trivial_string<char_type>::operator docstring() const;
template<typename Char>
basic_string<Char, char_traits<Char>, allocator<Char> >
trivial_string<Char>::str() const
trivial_string<Char>::operator basic_string<Char, char_traits<Char>, allocator<Char> >() const
{
if (use_sso())
return basic_string<Char, char_traits<Char>, allocator<Char> >(
@ -172,9 +172,49 @@ template bool operator<(trivial_string<char> const &,
template bool operator<(trivial_string<char_type> const &,
trivial_string<char_type> const &);
template <typename Char>
bool operator<(trivial_string<Char> const & lhs, trivial_string<Char> const &rhs)
bool operator<(trivial_string<Char> const & lhs, trivial_string<Char> const & rhs)
{
return lhs.compare(rhs) < 0;
}
template bool operator==(trivial_string<char> const &,
trivial_string<char> const &);
template bool operator==(trivial_string<char_type> const &,
trivial_string<char_type> const &);
template <typename Char>
bool operator==(trivial_string<Char> const & lhs, trivial_string<Char> const & rhs)
{
return lhs.compare(rhs) == 0;
}
template bool operator==(trivial_string<char> const &, char const *);
template bool operator==(trivial_string<char_type> const &, char_type const *);
template <typename Char>
bool operator==(trivial_string<Char> const & lhs, Char const * rhs)
{
return lhs.compare(trivial_string<Char>(rhs)) == 0;
}
template bool operator==(char const *, trivial_string<char> const &);
template bool operator==(char_type const *, trivial_string<char_type> const &);
template <typename Char>
bool operator==(Char const * lhs, trivial_string<Char> const & rhs)
{
return rhs.compare(trivial_string<Char>(lhs)) == 0;
}
template ostream & operator<<(ostream &, trivial_string<char> const &);
template odocstream & operator<<(odocstream &, trivial_string<char_type> const &);
template <typename Char>
basic_ostream<Char, char_traits<Char> > &
operator<<(basic_ostream<Char, char_traits<Char> > & os, trivial_string<Char> const & s)
{
return os << basic_string<Char, char_traits<Char>, allocator<Char> >(s);
}
} // namespace lyx
#endif

View File

@ -14,6 +14,7 @@
#include "support/strfwd.h"
#ifdef STD_STRING_USES_COW
#include <cstdlib>
namespace lyx {
@ -31,6 +32,9 @@ namespace lyx {
* This class should not be used for anything else than providing thread-safety.
* It should be removed as soon as LyX requires C++11, and all supported STL
* implementations provide a C++11 conformant std::basic_string.
*
* If you change anything in this class please ensure that the unit test
* tests/check_trivstring.cpp still tests 100% of the public interface.
*/
template <typename Char> class trivial_string
{
@ -56,7 +60,7 @@ public:
/// Is this string ordered before, at the same position or after \p other?
int compare(trivial_string const & other) const;
/// Create a copy as std::basic_string
std::basic_string<Char, std::char_traits<Char>, std::allocator<Char> > str() const;
operator std::basic_string<Char, std::char_traits<Char>, std::allocator<Char> >() const;
/// Return a C-compatible string, terminated by a 0 character.
/// This is never a copy and only valid for the life time of the trivial_string instance.
Char const * c_str() const;
@ -80,8 +84,25 @@ private:
/// The character storage
Char * data_;
};
template <typename Char> bool operator<(trivial_string<Char> const & lhs, trivial_string<Char> const &rhs);
/// Comparison operator (needed for std::set etc)
template <typename Char> bool operator<(trivial_string<Char> const & lhs, trivial_string<Char> const & rhs);
/// Equality operator
template <typename Char> bool operator==(trivial_string<Char> const & lhs, trivial_string<Char> const & rhs);
template <typename Char> bool operator==(trivial_string<Char> const & lhs, Char const * rhs);
template <typename Char> bool operator==(Char const * lhs, trivial_string<Char> const & rhs);
/// Stream output operator
template <typename Char>
std::basic_ostream<Char, std::char_traits<Char> > &
operator<<(std::basic_ostream<Char, std::char_traits<Char> > &, trivial_string<Char> const &);
#else
#include <string>
#endif
} // namespace lyx
#endif