I am using C++ to create a string class. I want the class to only accept the data types char and wchar_t and I want the compiler to catch any invalid data types during compile time using #error. I do not like using assert( ). How can I do this?
views:
196answers:
2
+9
A:
You can use a static assert. Boost provides one.
Maybe something like:
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
template <typename T>
class my_string
{
public:
// ...
private:
BOOST_STATIC_ASSERT((boost::is_same<T, char>::value ||
boost::is_same<T, wchar_t>::value));
};
int main(void)
{
my_string<char> chstr;
my_string<wchar_t> wstr;
// fails
my_string<int> istr;
}
If you can't use Boost, you can easily remake static-assert and is_same
:
// static assert failure
template <bool Predicate>
struct STATIC_ASSERT_FAILURE;
template <>
struct STATIC_ASSERT_FAILURE<true> {}; // only true is defined
// static assert test
template <unsigned TestResult>
struct static_assert {};
// static assert macro
#define STATIC_ASSERT(x) typedef static_assert< \
sizeof(STATIC_ASSERT_FAILURE<(x)>)> \
_static_assert_test_
// value is true if T and U are the same type
template <typename T, typename U>
struct is_same
{
static const bool value = false;
};
template <typename T>
struct is_same<T, T>
{
static const bool value = true;
};
template <typename T>
class my_string
{
public:
// ...
private:
STATIC_ASSERT((is_same<T, char>::value || is_same<T, wchar_t>::value));
};
int main(void)
{
my_string<char> chstr;
my_string<wchar_t> wstr;
// fails
my_string<int> istr;
}
Note, if you use a static assert in the same namespace twice, you'll get a name collision. You' have to use a more sophisticated version that uses a macro such as __COUNTER__
to generate unique names.
The above works in both GCC 4.4 and Visual Studio 2008.
GMan
2009-11-03 01:08:01
Actually, I would recommend BOOST_MPL_ASSERT_MSG, since the compiler-issued message is then more readable.
Matthieu M.
2009-11-03 08:44:08
+4
A:
You can play some tricks with specialization. First declare but don't define a template.
template <class C> class limiter;
Then specialize it for char and wchar_t and define some property.
template <>
class limiter<char>
{
public:
typedef char limit_type;
}
template <>
class limiter<wchar_t>
{
public:
typedef wchar_t limit_type;
}
In your string class, you can then reference:
template <class TYPE>
class mystring
{
typedef typename limiter<TYPE>::limit_type limit_type;
...
}
Since that will only be valid for char and wchar_t, no other types will be instantiable.
R Samuel Klatchko
2009-11-03 01:11:37
Thanks! This what more of the solution I wanted! I tried using specialization before I posted, but couldn't figure out how to generate compiler errors!This is my solution:[code]template <typename T>struct string_type;template <>struct string_type<char> {};template <>struct string_type<wchar_t> {};template <typename T>class string : string_type<T>{ ...};
Marlon
2009-11-03 01:24:33
This is a specific implementation of static assert, honestly. You should prefer using a more general solution.
GMan
2009-11-03 01:47:25
@GMan, sometimes general solution harder to understand rather than specific one. But your solution is better of course in general case.
Kirill V. Lyadvinsky
2009-11-03 05:44:20