This is a discussion on Code review within the C++ Programming forums, part of the General Programming Boards category; Originally Posted by Elysia Sure, it's what I do. But classes are better than free functions. In what way? And ...
Does it overload operators for integral types?
Does it overload a split function?
Does it overload a function to conver to/from unicode and ansi?
Classes are superior because they can use RAII and are OOP. It's better to do name.GetLength() instead of GetLength(name) IMHO.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^
Well, that's you. This one's for me.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^
I'm bumping into problems here, especially with the template specialization:
My definition is as follows:
And the template specialization is later:Code:template<typename T> class StrTraits { protected: typedef int (fnc_tprintf_s)(T* buffer, size_t sizeOfBuffer, const T* format, ...); typedef int (fnc_tvprintf_s)(T* buffer, size_t sizeOfBuffer, const T* format, va_list argptr); typedef int (fnc_tcscmp)(const T* string1, const T* string2); typedef int (fnc_tvcprintf)(const T* format, va_list argptr); static const T* UINT64_T_IDENTIFIER; static const T* INT64_T_IDENTIFIER; static const T* LONG_IDENTIFIER; uint32_t GetLength(const T* strData) const; // { return strlen(strData); } fnc_tprintf_s* GetFormatFunction(const T*) const; fnc_tvprintf_s* GetVFormatFunction(const T*) const; fnc_tvcprintf* GetVFormatCountFunction(const T*) const; fnc_tcscmp* GetCompareFunction(const T*) const; };
But it gives me load of linking errors for doing so. Multiple symbols!Code:template<> uint32_t StrTraits<char>::GetLength(const char* strData) const { return strlen(strData); } template<> uint32_t StrTraits<wchar_t>::GetLength(const wchar_t* strData) const { return wcslen(strData); } template<> typename StrTraits<char>::fnc_tprintf_s* StrTraits<char>::GetFormatFunction(const char*) const { return &sprintf_s; } template<> typename StrTraits<wchar_t>::fnc_tprintf_s* StrTraits<wchar_t>::GetFormatFunction(const wchar_t*) const { return &swprintf_s; } template<> typename StrTraits<char>::fnc_tvprintf_s* StrTraits<char>::GetVFormatFunction(const char*) const { return &vsprintf_s; } template<> typename StrTraits<wchar_t>::fnc_tvprintf_s* StrTraits<wchar_t>::GetVFormatFunction(const wchar_t*) const { return &vswprintf_s; } template<> typename StrTraits<char>::fnc_tvcprintf* StrTraits<char>::GetVFormatCountFunction(const char*) const { return &_vscprintf; } template<> typename StrTraits<wchar_t>::fnc_tvcprintf* StrTraits<wchar_t>::GetVFormatCountFunction(const wchar_t*) const { return &_vscwprintf; } template<> typename StrTraits<char>::fnc_tcscmp* StrTraits<char>::GetCompareFunction(const char*) const { return &strcmp; } template<> typename StrTraits<wchar_t>::fnc_tcscmp* StrTraits<wchar_t>::GetCompareFunction(const wchar_t*) const { return &wcscmp; }
All I can say is that template specialization is not my strong side >_<Code:error LNK2005: "protected: unsigned int __thiscall Strings::StrTraits<char>::GetLength(char const *)const " already defined in Stuff.obj error LNK2005: "protected: unsigned int __thiscall Strings::StrTraits<wchar_t>::GetLength(wchar_t const *)const " already defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<char>::GetFormatFunction(char const *)const )(char *,unsigned int,char const *,...)" already defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<wchar_t>::GetFormatFunction(wchar_t const *)const )(wchar_t *,unsigned int,wchar_t const *,...)" already defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<char>::GetVFormatFunction(char const *)const )(char *,unsigned int,char const *,char *)" already defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<wchar_t>::GetVFormatFunction(wchar_t const *)const )(wchar_t *,unsigned int,wchar_t const *,char *)" already defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<char>::GetVFormatCountFunction(char const *)const )(char const *,char *)" already defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<wchar_t>::GetVFormatCountFunction(wchar_t const *)const )(wchar_t const *,char *)" already defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<char>::GetCompareFunction(char const *)const )(char const *,char const *)" lready defined in Stuff.obj error LNK2005: "protected: int (__cdecl*__thiscall Strings::StrTraits<wchar_t>::GetCompareFunction(wchar_t const *)const )(wchar_t const *,wchar_t const *)" already defined in Stuff.obj
(Sorry for the long linking errors)
Further, if I do the implementation inline in the definition and add one specialization later, it works fine.
If I split the implementation to outside the definition (like how I did with CTmplStringBase), it also gives errors.
EDIT: Dammit, now I'm even having more problems.
All of the template functions which are split from the definition fails to work.
Last edited by Elysia; 05-10-2008 at 05:33 AM.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^
Why do you derive your template class from your traits template? Usually the trait is used as a template parameter in a template class, since this latter is the whole reason for making traits!
Further, I think that typedefs are better than declaring static variables? Static variables are more used by value traits, e.g. define a default initializer. Your traits are more type traits.
I would expect something like:
Or did I miss the whole point?Code://general traits declaration template<> struct StrTraits; // trait specialized template<UINT64_T_IDENTIFIER> struct StrTraits { typedef UINT64_T_IDENTIFIER myIdentifier; }; template<typename T, typename traits = StrTraits<T> > class CTmplStringBase: { public: CTmplStringBase(const CStringT< StrTraitMFC_DLL<typename traits::myIdentifier> >& strSrc); };
ps: a nice book about this is C++ Templates, Vandevoorde&Josuttis, 2003.
if anyone knows a similar or maybe even better book....
Last edited by MarkZWEERS; 05-10-2008 at 06:13 AM. Reason: reference
Don't know, I could do it that way, too.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^
Only if I get your idea right: you want to create some sort of mapping between your parameter 'T' and one of the three integers in your struct? Then the typedef seems more appropriate.Don't know, I could do it that way, too.
Well, as you could see in the code. If T = wchar_t, then I need wchar_t functions and wchar_t character constants. That's why I need this whole specialization mess in the first place.
So it seems that your idea is right.
Actually, when I think about it, I don't think it will work.
They are string literals that I need, depending on the type of T:
Code:template<> const char* StrTraits<char>::UINT64_T_IDENTIFIER = "%I64u"; template<> const char* StrTraits<char>::INT64_T_IDENTIFIER = "%I64i"; template<> const char* StrTraits<char>::LONG_IDENTIFIER = "%li"; template<> const wchar_t* StrTraits<wchar_t>::UINT64_T_IDENTIFIER = L"%I64u"; template<> const wchar_t* StrTraits<wchar_t>::INT64_T_IDENTIFIER = L"%I64i"; template<> const wchar_t* StrTraits<wchar_t>::LONG_IDENTIFIER = L"%li";
Last edited by Elysia; 05-10-2008 at 06:30 AM.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^
Ok just for fun: you can also specify behavior of member functions / operators by simply calling functions in "behavior traits". These are usually called "policy classes".
For example:
The operator has been declared 'friend' for correspondence with the static function 'add' , you could also pass '*this' (don't forget the * ) as _first_ argument.Code://general traits declaration template<> struct StrTraits; // trait specialized template<UINT64_T_IDENTIFIER> struct StrTraits { typedef UINT64_T_IDENTIFIER myIdentifier; }; //policy for addition template<> struct StrAdd; template<UINT64_T_IDENTIFIER> struct StrAdd { static UINT64_T_IDENTIFIER add( UINT64_T_IDENTIFIER const& arg1 , UINT64_T_IDENTIFIER const& arg2); }; template<typename T, typename traits = StrTraits<T>, typename sum = SumPolicy<T> > class CTmplStringBase: { public: CTmplStringBase(const CStringT< StrTraitMFC_DLL<typename traits::myIdentifier> >& strSrc); friend typename traits::myIdentifier operator+( typename traits::myIdentifier const& arg1, typename traits::myIdentifier const& arg2) { return sum::add( arg1, arg2); } };
Also notice that now you can only add two variables of the same type. Had I wanted to add variables of different types, I should have made a so-called "policy trait" : this is a mapping of type to function _and_ its return value. Example on request ;-)
A trait class and a policy class. Huh... Looks interesting, but I don't think it will help very much on the linking errors >_<
EDIT:
Does anyone get linking errors like me? I'm at a loss on how to correct them...
Last edited by Elysia; 05-10-2008 at 06:59 AM.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^
Oooops... I think I just violated one of Alexandrescus best coding practices:
44. Prefer writing nonmember nonfriend functions
http://www.ubookcase.com/book/Addiso...ch44.html#ch44
so no friend, no member. There wasn't any why I made it friend, just a bad habit of what "they" have always told me to do.
No need to worry about that. I hate non-member functions.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^
For what reason? I think Alexandrescu's argument is quite strong:I hate non-member functions.
"so that it will have the desirable property of accepting the same implicit conversions on its left-hand side and right-hand side parameters".
A very nice way to implement operators is the following:
http://www.ubookcase.com/book/Addiso...7lev1sec2.html
This is a very useful book!
All it does for me is invoke the implicit operator instead of working as it should.
Plus I'm not fan of "reversed" syntax.
If (myobj == something) <--- Right
if (something == myobj) <--- Wrong
It may, of course, matter on circumstances, but I don't usually need the "reversed" syntax.
But I especially hate non-member functions other than operators.
For information on how to enable C++11 on your compiler, look here.
よく聞くがいい!私は天才だからね! ^_^