diff --git a/CMakeLists.txt b/CMakeLists.txt index 0db64f7341..8be95c709a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/config/lyxinclude.m4 b/config/lyxinclude.m4 index 95cb1f09aa..01d57bc727 100644 --- a/config/lyxinclude.m4 +++ b/config/lyxinclude.m4 @@ -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) diff --git a/src/support/Makefile.am b/src/support/Makefile.am index ac43818e3e..3ba391f6fc 100644 --- a/src/support/Makefile.am +++ b/src/support/Makefile.am @@ -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 ; \ diff --git a/src/support/strfwd.h b/src/support/strfwd.h index 10411cda22..94f2136403 100644 --- a/src/support/strfwd.h +++ b/src/support/strfwd.h @@ -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 class trivial_string; +typedef trivial_string trivstring; +typedef trivial_string trivdocstring; +#else +typedef std::string trivstring; +typedef docstring trivdocstring; +#endif + } // namespace lyx #endif diff --git a/src/support/tests/CMakeLists.txt b/src/support/tests/CMakeLists.txt index 990e5385b5..2d1998927f 100644 --- a/src/support/tests/CMakeLists.txt +++ b/src/support/tests/CMakeLists.txt @@ -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") diff --git a/src/support/tests/check_trivstring.cpp b/src/support/tests/check_trivstring.cpp index e6a23d5603..f2c44e44ad 100644 --- a/src/support/tests/check_trivstring.cpp +++ b/src/support/tests/check_trivstring.cpp @@ -1,6 +1,7 @@ #include #include "../trivstring.h" +#include "../docstring.h" #include @@ -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(); } diff --git a/src/support/tests/regfiles/trivstring b/src/support/tests/regfiles/trivstring index e23bd5526c..28b6085730 100644 --- a/src/support/tests/regfiles/trivstring +++ b/src/support/tests/regfiles/trivstring @@ -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 diff --git a/src/support/trivstring.cpp b/src/support/trivstring.cpp index 71085ee04e..fa714e9402 100644 --- a/src/support/trivstring.cpp +++ b/src/support/trivstring.cpp @@ -13,6 +13,7 @@ #include "support/trivstring.h" #include "support/docstring.h" +#ifdef STD_STRING_USES_COW #include using namespace std; @@ -136,11 +137,10 @@ int trivial_string::compare(trivial_string const & other) const } -template string trivial_string::str() const; -template docstring trivial_string::str() const; +template trivial_string::operator string() const; +template trivial_string::operator docstring() const; template -basic_string, allocator > -trivial_string::str() const +trivial_string::operator basic_string, allocator >() const { if (use_sso()) return basic_string, allocator >( @@ -172,9 +172,49 @@ template bool operator<(trivial_string const &, template bool operator<(trivial_string const &, trivial_string const &); template -bool operator<(trivial_string const & lhs, trivial_string const &rhs) +bool operator<(trivial_string const & lhs, trivial_string const & rhs) { return lhs.compare(rhs) < 0; } + +template bool operator==(trivial_string const &, + trivial_string const &); +template bool operator==(trivial_string const &, + trivial_string const &); +template +bool operator==(trivial_string const & lhs, trivial_string const & rhs) +{ + return lhs.compare(rhs) == 0; +} + + +template bool operator==(trivial_string const &, char const *); +template bool operator==(trivial_string const &, char_type const *); +template +bool operator==(trivial_string const & lhs, Char const * rhs) +{ + return lhs.compare(trivial_string(rhs)) == 0; +} + + +template bool operator==(char const *, trivial_string const &); +template bool operator==(char_type const *, trivial_string const &); +template +bool operator==(Char const * lhs, trivial_string const & rhs) +{ + return rhs.compare(trivial_string(lhs)) == 0; +} + + +template ostream & operator<<(ostream &, trivial_string const &); +template odocstream & operator<<(odocstream &, trivial_string const &); +template +basic_ostream > & +operator<<(basic_ostream > & os, trivial_string const & s) +{ + return os << basic_string, allocator >(s); +} + } // namespace lyx +#endif diff --git a/src/support/trivstring.h b/src/support/trivstring.h index 4123d32ed1..491546992e 100644 --- a/src/support/trivstring.h +++ b/src/support/trivstring.h @@ -14,6 +14,7 @@ #include "support/strfwd.h" +#ifdef STD_STRING_USES_COW #include 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 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, std::allocator > str() const; + operator std::basic_string, std::allocator >() 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 bool operator<(trivial_string const & lhs, trivial_string const &rhs); +/// Comparison operator (needed for std::set etc) +template bool operator<(trivial_string const & lhs, trivial_string const & rhs); + + +/// Equality operator +template bool operator==(trivial_string const & lhs, trivial_string const & rhs); +template bool operator==(trivial_string const & lhs, Char const * rhs); +template bool operator==(Char const * lhs, trivial_string const & rhs); + + +/// Stream output operator +template +std::basic_ostream > & +operator<<(std::basic_ostream > &, trivial_string const &); +#else +#include +#endif + } // namespace lyx #endif