Template: Vermeidung partieller Spezialisierung bei char/wchar_t
-
Gibt es eine Möglichkeit hier Coderedundanz zu vermeiden?
template< typename char_type > struct S { static const char_type* text; } template< typename char_type > const char_type* S< char_type >::text= "argh"; int main() { S<char> s1; S<wchar_t> s2; }Problem hierbei ist natürlich, dass bei s2 das L fehlt (L"argh").
Also müsste man es bspw. so erweitern:template< typename char_type > struct S { static const char_type* text; } template< typename char_type > const char_type* S< char_type >::text= "argh"; template< > struct S< wchar_t > { static const wchar_t * text; } const char_type* S< wchar_t >::text= L"argh"; int main() { S<char> s1; S<wchar_t> s2; }Und das alles zwecks einem "L". Gibt es irgendein Möglichkeit (auch gerne mit boost) hier eine whar_t-Spezialisierung zu umgehen?
Das Problem bei mir ist, dass die struct derzeit ~20 char*-Variablen enthält. Und zudem ein paar std::vector< char_type >, die mit boost::list_of initialisiert werden...
-
evtl. ungefähr so:
struct MultiLiteral { /*constexpr*/ MultiLiteral(const char* str, const wchar_t* wstr) : str(str), wstr(wstr) {} /*constexpr*/ operator const char*() const { return str; } /*constexpr*/ operator const wchar_t*() const { return wstr; } const char* str; const wchar_t* wstr; }; #define MULTILITERAL( x ) MultiLiteral( x, L##x ) template< typename char_type > struct S { static const char_type* text; } template< typename char_type > const char_type* S< char_type >::text= MULTILITERAL( "argh" ); int main() { S<char> s1; S<wchar_t> s2; }Das müsste eigentlich auch mit list_of funktionieren. Mit selbstdefinierten Literalen könnte man in C++0x auch noch das Makro loswerden.
-
danke camper!!! Damit ist mein Tag schon halb gerettet

Bei list_of habe ich allerdings noch probleme. Der vector Typ ist anders als ich dachte vom Typ string_type, wobei die strings alle ctors mit char_type besitzen
template< typename string_type, typename char_type > struct S2 { static const std::vector< string_type > vec; }; template< typename string_type, typename char_type > const std::vector< string_type > S2< string_type, char_type >::vec = boost::assign::list_of( MULTILITERAL("A") )( MULTILITERAL("B") ); int main() { S2< std::string , char >::vec; S2< std::wstring, wchar_t >::vec; }--> cannot convert parameter 2 from 'std::allocator<_Ty>::value_type' to 'const std::string &'
boost::assign::list_of( (const char_type*)MULTILITERAL("A") )( (const char_type*)MULTILITERAL("B") );Funktionierte dann, gibt es hierfür evtl eine elegantere Lösung

-
muffmolch schrieb:
Hast du da evtl auch eine schnelle Lösung

noch mehr Konvertierungsfunktionen.
struct MultiLiteral { /*constexpr*/ MultiLiteral(const char* str, const wchar_t* wstr) : str(str), wstr(wstr) {} /*constexpr*/ operator const char*() const { return str; } /*constexpr*/ operator const wchar_t*() const { return wstr; } operator std::string() const { return str; } operator std::wstring() const { return wstr; } const char* str; const wchar_t* wstr; };DIe ursprüngliche Version funktioniert für Strings deshalb nicht, weil für diese Konvertierung dann zwei Funktionsaufrufe nötig wären (Konvertierungsoperator+Konvertierungskonstruktor), was nicht zulässig ist.
-
Ja, aber leider sind es nicht nur wstring und string

-
struct MultiLiteral { /*constexpr*/ MultiLiteral(const char* str, const wchar_t* wstr) : str(str), wstr(wstr) {} template <typename T> operator T() const { return convert((T*)0); } template <typename T> typename boost::enable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return str; } template <typename T> typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr; } const char* str; const wchar_t* wstr; };
-
Es sind ca 8 unterschieldiche Stringklassen (sind allesamt nicht von mir).
edit: code entfernt
-
Siehe letzte Änderung.
-
alter Falter! Respekt! Danke noch mal!
ein kleiner Fehler ist noch in der Klasse:
typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr; } soll vermutlich heißen: typename boost::disable_if<boost::is_convertible<const wchar*, T>, T>::type convert(T*) const { return wstr; }
-
muffmolch schrieb:
ein kleiner Fehler ist noch in der Klasse:
typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr; } soll vermutlich heißen: typename boost::disable_if<boost::is_convertible<const wchar*, T>, T>::type convert(T*) const { return wstr; }Das ist schon so beabsichtigt. Es wird absichtlich nur auf Konvertierbarkeit in char* geprüft. Auf diese Weise vermeiden wir Probleme falls ein String zufällig mit beiden initialisiert werden kann - in diesem Falle wird char bevorzugt. Beachte, dass es einmal enable_if, und dann disable_if heißt. Man kann das nat. auch anders machen, z.B. im Falle von Mehrdeutigkeit ganz bewusst einen Fehler produzieren.
-
Wenn Geschwindigkeit gefragt ist, kann man das noch ein bisschen verbessern, sofern die Strings alle auch mit Iteratoren initialisiert werden können):
struct MultiLiteral { template <std::size_t N> /*constexpr*/ MultiLiteral(const char (&str)[N], const wchar_t (&wstr)[N]) : str(str), wstr(wstr), n(N) {} template <typename T> /*constexpr*/ operator T() const { return convert((T*)0); } template <typename T> /*constexpr*/ typename boost::enable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return str(str, str + n); } template <typename T> /*constexpr*/ typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr(wstr, wstr + n); } const char* str; const wchar_t* wstr; std::size_t n; };So kann sich die Stringklasse den Aufruf von (w)strlen sparen.
-
ok! werd ich gleich mal testen. Momentan stolpere ich darüber, dass ich bei boost::list_of ambiguous calls erhalte...
struct A { A( const char * ) {} A( const wchar_t * ) {} A& operator=( A & ) { return *this; } A& operator=( const char* t ) { return *this; } A& operator=( const wchar_t* t ) { return *this; } }; int main( int, char * ) { A a = ML("ZESZ"); //funktioniert vector< A > ddd1 = boost::assign::list_of(ML("A"))(ML("B")); //ambiguous //main.cpp: could be 'A &A::operator =(const wchar_t *)' //main.cpp: or 'A &A::operator =(const char *)' }
-
A& operator=( [b][i]const[/i][/b] A & )Ach ja, im Falle von Mehrdeutigkeit (char/wchar_t) hilft das enable_if doch nicht weiter - denn der Compiler weiß trotzdem nicht, welchen Konvertierungsoperator er benutzen muss.
In diesem Falle bleibt wahrscheinlich doch nur, einen extra Konvertierungsoperator für jede einzelne Stringklasse zu schreiben.
-
genau... hab ich auch grad festgestellt. const A& ist natürlich im Code so (war ja nur ein dummy Bsp).
-
Wenn die Stringklassen alle Templateklassen sind (und ansonsten nicht noch zusätzliche Konvertierungen haben, die stören könnten), könnte man den Konvertierungsoperator entsprechend einschränken:
#include <iostream> #include <boost/type_traits.hpp> #include <boost/utility.hpp> #include <boost/assign.hpp> struct MultiLiteral { constexpr MultiLiteral(const char* str, const wchar_t* wstr) : str(str), wstr(wstr) {} template <template <typename...> class String, typename... Args> operator String<Args...>() const { return convert((String<Args...>*)0); } template <typename T> typename boost::enable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return str; } template <typename T> typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr; } const char* str; const wchar_t* wstr; }; #define ML( x ) MultiLiteral( x, L##x ) template <typename T> struct A { A( const char * ) {} A( const wchar_t * ) {} A& operator=( const A & ) { return *this; } A& operator=( const char* t ) { return *this; } A& operator=( const wchar_t* t ) { return *this; } }; #include <vector> using namespace std; int main() { A<int> a = ML("ZESZ"); A<int> b( ML("ZESZ") ); b = ML("ZESZ"); vector< A<int> > ddd1 = boost::assign::list_of(ML("A"))(ML("B")); //ambiguous //main.cpp: could be 'A &A::operator =(const wchar_t *)' //main.cpp: or 'A &A::operator =(const char *)' }Läuft mit g++ 4.5.2.
Wenn der Compiler noch keine variadic Templates kennt, müsste man wiederum die Parameter konkreter spezifizieren.
-
nein, das schluckt mein Compiler nicht (VS2008).
Und bei der anderen Klasse erhalte ich einen Linkerfehler bei:
string s = ML("test"); //unresolved external symbol "public: __thiscall CMultiliteral::operator<char const *> char const *(void)const " //unresolved external symbol "public: __thiscall CMultiliteral::operator<wchar_t const *> wchar_t const *(void)const " string s= (const char*)ML("test"); //-funktioniert
-
Vollständigen Code bitte.
-
Mit der Klasse aus deinem Beitrag vom 13:28:22 29.06.2011. Allerdings funktioniert es momentan.. Der Grund für den Linkerfehler ist mir nicht so ersichtlich. Wird wohl einmal mehr ein VS Problem gewesen sein, denn nach einem Neustart des Rechners wurde derselbe Code anstandslos übersetzt.. In dieseem Momenten wünsche ich mit den gcc oder icc zurück!