Шаблон специализации для указателя на символ?
boost::lexical_cast
это отличный инструмент, но в моем приложении я столкнулся с ограничением в string -> bool
преобразование, которое беспокоит меня. Мне нужно конвертировать все строки, как "0"
, "false"
а также "FALSE"
в false
а также "1"
, "true"
а также "TRUE"
в true
,
boost::lexical_cast
только поддержка преобразования из / в "0"
а также "1"
, Поэтому я хотел написать свою собственную функцию преобразования, которая, кажется, работает нормально:
bool str_to_bool(const std::string &str)
{
if(str == "1" || str == "true" || str == "TRUE")
return true;
else if(str == "0" || str == "false" || str == "FALSE")
return false;
else
throw std::runtime_error("Bad cast from std::string to bool!");
}
Теперь я хочу написать обертку boost::lexical_cast
и написать свой собственный шаблон специализации для него. Вот что у меня так далеко:
template<typename Target, typename Source>
inline Target my_cast(const Source& src)
{
return boost::lexical_cast<Target>(src);
}
template<>
inline bool my_cast(const std::string& src)
{
return str_to_bool(src);
}
Это прекрасно работает для целых чисел или std:: string, но очевидно не работает для строковых литералов или символьных указателей:
int main(int argc, char* argv[])
{
std::cout << my_cast<bool>(1) << std::endl; //OK
std::cout << my_cast<bool>(std::string("true")) << std::endl; //OK
std::cout << my_cast<bool>("true") << std::endl; //Fail!
return 0;
}
Поэтому я попытался написать еще одну специализацию для char *
но он не компилируется!
//does not compile!
template<>
inline bool my_cast(const char*& src)
{
return str_to_bool(src);
}
Как правильно поддерживать как std:: string, так и char *
?
РЕДАКТИРОВАТЬ 1: название было глупо. Починил это.
РЕДАКТИРОВАТЬ 2: Я позаимствовал решение от самого повышения. Опубликовано как новый ответ.
4 ответа
Вот решение, которое работает. Я поняла от boost::lexical_cast
сам:
template<class T>
struct array_to_pointer_decay
{
typedef T type;
};
template<class T, std::size_t N>
struct array_to_pointer_decay<T[N]>
{
typedef const T * type;
};
template<typename Target, typename Source>
Target my_cast_internal(const Source& s)
{
return boost::lexical_cast<Target>(s);
}
template<>
inline bool my_cast_internal(const std::string& src)
{
return str_to_bool(src);
}
template<>
inline bool my_cast_internal(const char* const& src)
{
return str_to_bool(src);
}
template<typename Target, typename Source>
inline Target my_cast(const Source& s)
{
typedef typename array_to_pointer_decay<Source>::type src;
return my_cast_internal<Target, src>(s);
}
Основная проблема заключается в работе с типами массивов. array_to_pointer_decay
преобразует любой тип массива в соответствующий тип указателя. Остальное сейчас легко.
Если вы говорите это:
template<>
inline bool my_cast<bool, std::string>(std::string const & src)
{
return str_to_bool(src);
}
template<>
inline bool my_cast<bool, const char *>(const char * const & src)
{
return str_to_bool(src);
}
Тогда хотя бы вы можете сделать следующую работу:
int main(int argc, char* argv[])
{
const char * const q = "true";
std::cout << my_cast<bool>(q) << std::endl; //Fail!
return 0;
}
Обновление: Вуаля:
typedef char FT[5];
template<>
inline bool my_cast<bool, FT>(const FT & src)
{
return str_to_bool(src);
}
Вам нужно взять const char*
не const char*&
, Изменяемая ссылка lvalue здесь будет привязана только к lvalue, тогда как распад от типа массива, которым на самом деле является строковый литерал, будет давать только значение rvalue. const char*
, к которому можно привязать только константную ссылку.
Позвольте мне добавить это как новый ответ... версия со стиранием типов!
Для C++98/03
/* Core caster */
bool str_to_bool(const std::string &str)
{
if(str == "1" || str == "true" || str == "TRUE")
return true;
else if(str == "0" || str == "false" || str == "FALSE")
return false;
else
throw std::runtime_error("Bad cast from std::string to bool!");
}
/* Type erasing scaffold */
struct TypeEraseBase
{
virtual bool cast() const = 0;
virtual ~TypeEraseBase() { }
};
template <typename T>
struct TypeEraseImpl : public TypeEraseBase
{
TypeEraseImpl(const T & tt) : t(tt) { }
virtual bool cast() const { return boost::lexical_cast<T>(t); }
private:
const T & t;
};
/* Specializations go here */
template <>
struct TypeEraseImpl<std::string> : public TypeEraseBase
{
TypeEraseImpl(const std::string & tt) : t(tt) { }
virtual bool cast() const { return str_to_bool(t); }
private:
const std::string & t;
};
template <size_t N>
struct TypeEraseImpl<char[N]> : public TypeEraseBase
{
TypeEraseImpl(const char (& tt)[N]) : t(tt) { }
virtual bool cast() const { return str_to_bool(std::string(t)); }
private:
const char (& t)[N];
};
template <>
struct TypeEraseImpl<const char *> : public TypeEraseBase
{
TypeEraseImpl(const char * const & tt) : t(tt) { }
virtual bool cast() const { return str_to_bool(std::string(t)); }
private:
const char * const & t;
};
/* User interface class */
struct my_cast
{
template <typename T> my_cast(const T & tt)
: pt(new TypeEraseImpl<T>(tt))
{
}
~my_cast() { if (pt) delete pt; }
inline bool cast() const { return pt->cast(); }
private:
const TypeEraseBase * const pt;
};
// Usage example
int main()
{
const char * const q = "true";
std::cout << my_cast(1).cast() << std::endl;
std::cout << my_cast(std::string("true")).cast() << std::endl;
std::cout << my_cast("true").cast() << std::endl;
std::cout << my_cast(q).cast() << std::endl;
return 0;
}
Типовая версия, шаблонный тип возврата
#include <string>
#include <stdexcept>
#include <iostream>
#include <ostream>
#include <boost/lexical_cast.hpp>
template <typename T> struct is_string : std::false_type { };
template <> struct is_string<std::string> : std::true_type { };
template <> struct is_string<const char *> : std::true_type { };
template <std::size_t N> struct is_string<char[N]> : std::true_type { };
/* The actual caster class */
template <typename T, bool B> struct to_bool
{
static inline bool cast(const T & t)
{
return boost::lexical_cast<T>(t);
}
};
template <typename T> struct to_bool<T, true>
{
static inline bool cast(const T & t)
{
const std::string str(t);
if(str == "1" || str == "true" || str == "TRUE")
return true;
else if(str == "0" || str == "false" || str == "FALSE")
return false;
else
throw std::runtime_error("Bad cast from std::string to bool!");
}
};
/* Type erasing helper class */
template <typename Target>
struct TypeEraseBase
{
virtual Target cast() const = 0;
virtual ~TypeEraseBase() { }
};
template <typename T, typename Target>
struct TypeEraseImpl : public TypeEraseBase<Target>
{
TypeEraseImpl(const T & tt) : t(tt) { }
virtual Target cast() const { return boost::lexical_cast<T>(t); }
private:
const T & t;
};
template <typename T>
struct TypeEraseImpl<T, bool> : public TypeEraseBase<bool>
{
TypeEraseImpl(const T & tt) : t(tt) { }
virtual bool cast() const { return to_bool<T, is_string<T>::value>::cast(t); }
private:
const T & t;
};
/* User interface class */
template <typename Target>
struct my_cast
{
template <typename T> my_cast(const T & tt)
: pt(new TypeEraseImpl<T, Target>(tt)) { }
~my_cast() { if (pt) delete pt; }
inline Target cast() const { return pt->cast(); }
private:
const TypeEraseBase<Target> * const pt;
};
template <typename Target>
std::ostream & operator<<(std::ostream & stream, const my_cast<Target> & c)
{ return stream << c.cast(); }
/* Usage */
int main()
{
const char * const q = "true";
std::cout << my_cast<bool>(1) << std::endl;
std::cout << my_cast<bool>(std::string("true")) << std::endl;
std::cout << my_cast<bool>("true") << std::endl;
std::cout << my_cast<bool>(q) << std::endl;
return 0;
}