Since I discovered boost::lexical_cast all conversions are a breeze. That's until trying to convert a tuples elements into a string. Like Int2String or Double2String I want a way to generate a single string from a tuple of arbitrary number of elements
Since the subject of conversion has arbitrary (yet compile time known) dimension, after some research I've looked into boost::fusion and found this solution :
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/noncopyable.hpp>
#include <boost/fusion/include/for_each.hpp>
template <class Sequence>
std::string StringFromSequence(const Sequence &seq)
{
std::string result;
boost::fusion::for_each(seq, toString(result));
return result;
}
Where toString is a functor applying the lexical cast to the objects that's been called for :
struct toString: boost::noncopyable
{
explicit toString(std::string& res) : result(res)
{
}
template <class T>
void operator()(const T& v)
{
result += boost::lexical_cast<std::string>(v);
}
private:
std::string &result;
};
When trying to use that though
std::tuple<int, char, double> tup{ 1, 'a', 2.2 };
toString(tup);
I get a compilation error
error C2893: Failed to specialize function template 'enable_if, void>::type boost::fusion::for_each(const Sequence &,const F &)'
Can someone spot and correct the error ?
Are there any (maybe boost free) alternatives ? (it would take compile time recursion and I was hopefully trying to avoid all that boilerplate code)
Since this question is tagged C++11, here's my take at it:
#include <iostream>
#include <string>
#include <tuple>
template<typename T, T...>
struct integer_sequence { };
template<std::size_t N, std::size_t... I>
struct gen_indices : gen_indices<(N - 1), (N - 1), I...> { };
template<std::size_t... I>
struct gen_indices<0, I...> : integer_sequence<std::size_t, I...> { };
template<typename H>
std::string& to_string_impl(std::string& s, H&& h)
{
using std::to_string;
s += to_string(std::forward<H>(h));
return s;
}
template<typename H, typename... T>
std::string& to_string_impl(std::string& s, H&& h, T&&... t)
{
using std::to_string;
s += to_string(std::forward<H>(h));
return to_string_impl(s, std::forward<T>(t)...);
}
template<typename... T, std::size_t... I>
std::string to_string(const std::tuple<T...>& tup, integer_sequence<std::size_t, I...>)
{
std::string result;
int ctx[] = { (to_string_impl(result, std::get<I>(tup)...), 0), 0 };
(void)ctx;
return result;
}
template<typename... T>
std::string to_string(const std::tuple<T...>& tup)
{
return to_string(tup, gen_indices<sizeof...(T)>{});
}
int main(int argc, char** argv)
{
std::tuple<int, double, float> tup(1, 2.1, 3.2);
std::cout << to_string(tup) << std::endl;
}
If you want to stick with boost::lexical_cast, replace to_string with lexical_cast.
Live output on ideone
Sorry but I'm too lazy to enter the details of where you're making the mistakes, but here's a solution using fusion and C++14 polymorphic lambdas:
#include <tuple>
#include <string>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/fusion/adapted/std_tuple.hpp> // you're missing this huh? it's needed to use fusion with std::tuple
#include <boost/fusion/algorithm/iteration/for_each.hpp>
int main() {
using namespace std;
using namespace boost::fusion;
string result;
for_each(make_tuple(1, 'a', 2.2), [&result](auto &s) {
result += boost::lexical_cast<string>(s) + ' ';
});
cout << result << endl;
}
http://coliru.stacked-crooked.com/a/f110238a317eede9
Related
Is there a way to "expand" a vector variable in an initialization list to achieve something to the effect of
std::vector<int> tmp1{1,1,2,3,5};
std::vector<int> tmp2{11,tmp1,99};
//tmp2 == {11,1,1,2,3,5,99}
I know it could be done with std::copy or insert. Was wondering if there was list-initialization way achieve that. something like [11,*tmp1,99] in python
With this solution you can have something close the the syntax you want.
(And yes some of the work will be done at runtime)
#include <type_traits>
#include <vector>
namespace details
{
// recursive part
template<typename type_t, typename arg_t, typename... args_t>
void append_to_vector(std::vector<type_t>& vec, arg_t value, args_t&&... values)
{
if constexpr (std::is_same_v<arg_t, std::vector<type_t>>)
{
vec.insert(vec.end(), value.begin(), value.end());
}
else
{
vec.push_back(value);
}
if constexpr (sizeof...(args_t) > 0)
{
append_to_vector(vec, std::forward<args_t>(values)...);
}
}
}
template<typename type_t, typename... args_t>
std::vector<type_t> make_vector(args_t&&... values)
{
std::vector<type_t> retval;
if constexpr (sizeof...(args_t) > 0)
{
details::append_to_vector(retval, std::forward<args_t>(values)...);
}
return retval;
};
int main()
{
auto tmp1 = make_vector<int>(1, 1, 2, 3, 5);
auto tmp2 = make_vector<int>(11, tmp1, 99);
return 0;
}
One option could be to create a make_vector variadic function template that checks if the current argument supports std::begin(). If it supports std::begin(), use the vector's insert member function, otherwise, use emplace_back. That should make it possible to make a vector from any container (with the correct T).
First, one type trait to check if the current argument supports std::begin() and one that checks if it supports std::size(). std::size() will be used to calculate how much space the final vector will need. Reserving space is often used to avoid reallocating space (and moving elements) when populating vectors when the final (or minimal) amount of elements is known - as it is in this case.
#include <iterator>
#include <type_traits>
template<typename T>
class has_begin {
template<typename TT>
static auto test(int) ->
decltype( std::begin(std::declval<const TT&>()), std::true_type() );
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool has_begin_v = has_begin<T>::value;
template<typename T>
class has_size {
template<typename TT>
static auto test(int) ->
decltype( std::size(std::declval<const TT&>()), std::true_type() );
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool has_size_v = has_size<T>::value;
Then, the actual make_vector function template and helpers to calculate the actual size and determine what to do with one specific argument.
#include <utility>
#include <vector>
namespace detail {
template<class T, class Arg>
size_t make_vector_capacity(Arg&& arg) {
if constexpr(std::is_same_v<T, std::remove_cv_t<std::remove_reference_t<Arg>>>) {
// same type as in the vector, return 1
return 1;
} else if constexpr(has_size_v<Arg>) {
// a container supporting std::size
return std::size(arg);
} else if constexpr(has_begin_v<Arg>) {
// a container but not supporting std::size
return std::distance(std::begin(arg), std::end(arg));
} else {
// fallback
return 1;
}
}
template<class T, class Arg>
void make_vector_helper(std::vector<T>& v, Arg&& arg) {
if constexpr(std::is_same_v<T, std::remove_cv_t<std::remove_reference_t<Arg>>>) {
// same type as in the vector, insert it as-is
v.emplace_back(std::forward<Arg>(arg));
} else if constexpr(has_begin_v<Arg>) {
// arg supports std::begin, use insert
v.insert(v.end(), std::begin(arg), std::end(arg));
} else {
// fallback
v.emplace_back(std::forward<Arg>(arg));
}
}
} // namespace detail
template<class T, class... Args>
std::vector<T> make_vector(Args&&... args) {
std::vector<T> rv;
// a fold expression to calculate the capacity needed:
rv.reserve( (detail::make_vector_capacity<T>(args) + ...) );
// a fold expression to call make_vector_helper for each argument
(detail::make_vector_helper(rv, std::forward<Args>(args)), ...);
return rv;
}
A usage example, mixing different containers and single values of std::string:
#include <initializer_list>
#include <iostream>
#include <list>
#include <vector>
#include <string>
int main() {
using namespace std::string_literals;
const std::string arr[] = {"3"s, "4"s};
const std::vector vec{"5"s, "6"s, "7"s , "8"s};
const std::list list{"9"s,"10"s};
auto init = {"13"s, "14"s, "15"s};
const auto tmp2 = make_vector<std::string>("1"s, "2"s, arr, vec, list,
"11"s, "12"s, init );
for(const auto& e: tmp2) std::cout << e <<' ';
}
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Demo
No, it is not possible but you can make a helper that allows can do it:
#include <array>
#include <type_traits>
// Containerize MaybeContainer type if it not equal to T.
template<typename T, typename MaybeContainer>
using impl_containerize = std::conditional_t<std::is_same_v<T,std::decay_t<MaybeContainer>>,std::array<T,1>,MaybeContainer>;
// Concatenate containers
template<typename Container,typename...Args>
Container impl_construct(Args&&...args){
Container c;
(c.insert(c.end(), std::begin(args), std::end(args)), ...);
return c;
}
template<typename Container,typename...Args>
Container construct(Args&&...args){
using T = typename Container::value_type;
return impl_construct<Container, impl_containerize<T,Args>...>({std::forward<Args>(args)}...);
}
#include <iostream>
#include <vector>
int main()
{
std::vector<int> tmp1{1,3,5,7,9};
const auto tmp2 = construct<std::vector<int>>(2,4,tmp1,6);
for(const auto& e: tmp2){
std::cout<<e<<' ';
}
}
Output
2 4 1 3 5 7 9 6
I have a function with two template arguments, one for a vector data type (int, float, double, etc.) and one for an integer type (int, int16_t, uint32_t, etc.):
template <typename T, typename I>
void
add(std::vector<T> array, std::vector<I> idx) {
// ...
}
For tests, I would now like to loop over every possible combination of data/integer types, e.g.,
// pseudo code
for I in [int, int16_t, int32_t, ...] {
for T in [float, double, int, int16_t, ...] {
// create arguments a, b
add<T, I>(a, b);
}
}
Is it possible to loop over types at all? How?
There may be simpler ways to do this, but I would use the boost hana library, as follows:
#include <boost/hana/for_each.hpp>
#include <boost/hana/tuple.hpp>
#include <vector>
#include <iostream>
#include <boost/type_index.hpp>
namespace hana = boost::hana;
// for example's sake, just print the type
template <typename T, typename I>
void add(std::vector<T> array, std::vector<I> idx) {
using namespace boost::typeindex;
std::cout << type_id<T>().pretty_name() << " - " << type_id<I>().pretty_name() << std::endl;
}
int main() {
auto types1 = hana::tuple_t<int, int16_t, int32_t>;
auto types2 = hana::tuple_t<float, double, int, int16_t>;
hana::for_each(types1, [types2](auto t1) {
hana::for_each(types2, [t1](auto t2) {
using t1_type = typename decltype(t1)::type;
using t2_type = typename decltype(t2)::type;
add<t1_type, t2_type>({}, {});
});
});
}
If you have boost at hand then boost::hana is definitely the way to go. However, it's not very hard to reimplement that feature by hand if you have to:
#include <iostream>
template<typename... T>
struct iterator {
template<typename CB_T>
static void iterate(CB_T const& ) {}
};
template<typename first, typename... rest>
struct iterator<first, rest...> {
template<typename CB_T>
static void iterate(CB_T const& cb) {
cb(first());
iterator<rest...>::iterate(cb);
}
};
int main() {
iterator<int, float>::iterate([](auto const & v_1){
using v_1_t = decltype(v_1);
iterator<char, double>::iterate([&](auto const & v_2){
using v_2_t = decltype(v_2);
std::cout << typeid(v_1_t).name() << " vs " << typeid(v_2_t).name() << "\n";
});
});
return 0;
}
I have a vector of strings, each of which is the result of applying std::to_string to some basic datatype (eg char, int, double). I would like a function to undo this into a tuple of the appropriate types.
I have a simple function template to invert std::to_string:
template<typename T>
T from_string(std::string s)
{
}
template<>
int from_string<int>(std::string s)
{
return std::stoi(s);
}
template<>
double from_string<double>(std::string s)
{
return std::stod(s);
}
//... and more such specializations for the other basic types
I want a function like:
template<typename... Ts>
std::tuple<Ts> undo(const std::vector<std::string>>& vec_of_str)
{
// somehow call the appropriate specializations of from_string to the elements of vector_of_str and pack the results in a tuple. then return the tuple.
}
The function should behave like this:
int main()
{
auto ss = std::vector<std::string>>({"4", "0.5"});
auto tuple1 = undo<int, double>(ss);
std::tuple<int, double> tuple2(4, 0.5);
// tuple1 and tuple2 should be identical.
}
I think that I have to "iterate" over the parameters in Ts (perhaps the correct term is "unpack"), call the previous function, from_string for each one, and then package the results of each application of from_string into a tuple. I've seen (and used) examples that unpack a template parameter pack - they are usually recursive (but not in the usual way of a function calling itself), but I don't see how to do the rest.
An example:
#include <vector>
#include <string>
#include <tuple>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/trim.hpp>
template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
return {boost::lexical_cast<Ts>(boost::algorithm::trim_copy(values[Idxs]))...};
}
template<class... Ts>
std::tuple<Ts...> undo(std::vector<std::string> const& values) {
assert(sizeof...(Ts) == values.size());
return parse<Ts...>(values, std::make_index_sequence<sizeof...(Ts)>{});
}
int main() {
auto ss = std::vector<std::string>({"4", "0.5"});
auto tuple1 = undo<int, double>(ss);
std::tuple<int, double> tuple2(4, 0.5);
std::cout << (tuple1 == tuple2) << '\n';
assert(tuple1 == tuple2);
}
If the string values do not contain leading and/or trailing whitespace, then that call to boost::algorithm::trim_copy can be removed. It is there because boost::lexical_cast fails on whitespace.
Without boost::lexical_cast you will need to re-implement it, something like:
template<class T> T from_string(std::string const& s);
template<> int from_string<int>(std::string const& s) { return std::stoi(s); }
template<> double from_string<double>(std::string const& s) { return std::stod(s); }
// And so on.
template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
return {from_string<Ts>(values[Idxs])...};
}
For C++11 -- useful if you don't have C++14 (required by Maxim's solution), or in case you want to learn to implement recursive variadic templates:
#include <string>
#include <vector>
#include <tuple>
#include <cassert>
template <std::size_t N, typename T>
struct Undo
{
static void f(T& tuple, const std::vector<std::string>& vec_of_str)
{
Undo<N - 1, T>::f(tuple, vec_of_str);
std::get<N - 1>(tuple) = from_string<
typename std::tuple_element<N - 1, T>::type
>(vec_of_str[N - 1]);
}
};
template <typename T>
struct Undo<0, T>
{
static void f(T&, const std::vector<std::string>&)
{
}
};
template <typename... Ts>
std::tuple<Ts...> undo(const std::vector<std::string>& vec_of_str)
{
assert(vec_of_str.size() == sizeof...(Ts));
std::tuple<Ts...> ret;
Undo<sizeof...(Ts), std::tuple<Ts...>>::f(ret, vec_of_str);
return ret;
}
Has anyone ever combined the classic generic factory by Andrei Alexandrescu (page 208 of Chapter 8 in Modern C++ Design) with the 'multifunction' capabilities of Boost.TypeErasure? That is, the flexibility to have several creator function signatures that vary with respect to number and type of parameters (but still have the same return type and are known at compile time).
In other words, how to combine this slightly simplified generic Factory:
#include <map>
#include <utility>
#include <stdexcept>
template <class AbstractProduct, typename IdentifierType, typename ProductCreator>
class Factory
{
public:
bool Register(const IdentifierType& id, ProductCreator creator) {
return associations_.emplace(id, creator).second;
}
bool Unregister(const IdentifierType& id) {
return associations_.erase(id) == 1;
}
template <typename... Arguments>
AbstractProduct CreateObject(const IdentifierType& id, Arguments&& ... args) {
auto i = associations_.find(id);
if (i != associations_.end()) {
return (i->second)(std::forward<Arguments>(args)...);
}
throw std::runtime_error("Creator not found.");
}
private:
std::map<IdentifierType, ProductCreator> associations_;
};
with this (incomplete) function type erasure 'pattern':
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/callable.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/variant.hpp>
template<class... Sig>
using multifunction = any< mpl::vector< copy_constructible<>, typeid_<>, relaxed, callable<Sig>... > >;
using variant_type = boost::make_recursive_variant< void, double, ... >::type;
using function_type = multifunction<AbstractProduct(void), AbstractProduct(double), AbstractProduct(double, double)>;
class variant_handler
{
public:
void handle(const variant_type& arg) {
boost::apply_visitor(impl, arg);
}
void set_handler(function_type f) {
impl.f = f;
}
private:
struct dispatcher : boost::static_visitor<void>
{
template<class T>
void operator()(const T& t) { f(t); }
// For a vector, we recursively operate on the elements
void operator()(const vector_type& v)
{
boost::for_each(v, boost::apply_visitor(*this));
}
function_type f;
};
dispatcher impl;
};
So that ultimately one can use it like:
Factory<Arity*, int, ???> factory;
factory.Register(0, boost::bind( boost::factory<Nullary *>() ));
factory.Register(1, boost::bind( boost::factory<Unary *>(), _1 ));
auto x = factory.CreateObject(0);
auto y = factory.CreateObject(1, 0.5);
I haven't found an existing implementation in the wild, and I am currently stuck in my own attempt to make it. My first attempt made the mistake of trying to store the result of boost::bind() in the function_type, which resulted the same error to this SO question. I suspect the answer will require moving the ProductCreator template parameter to the Register function and doing something there.
So I guess I am ultimately looking for a full, working implementation of a generic multifunction factory, which may already exist and I just overlooked it. But any help with just getting it together would be really appreciated.
I would prefer a C++11 solution, but obviously C++14 is better than none, etc.
Thanks in advance for any help with this!
OK, I have a slightly ugly solution that doesn't use Boost.TypeErasure, it is C++14, but it does provide essentially the same functionality. It's multi-tiered, so the id numbering is per-factory (but you could number uniquely too).
I'll write more soon, but I really have to go to sleep right now...
#include <boost/functional/factory.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <cassert>
#include <map>
#include <tuple>
#include <type_traits>
#include <utility>
template <class AbstractProduct, typename IdentifierType, typename... ProductCreators>
class Factory
{
using AssociativeContainers = std::tuple<std::map<IdentifierType, boost::function<ProductCreators>>...>;
public:
template <typename Product, typename... Arguments>
bool Register(const IdentifierType& id, boost::function<Product(Arguments...)> creator) {
auto &foo = std::get<std::map<IdentifierType, boost::function<AbstractProduct(const Arguments&...)>>>(associations_);
return foo.emplace(id, creator).second;
}
// This function left as an exercise to the reader...
bool Unregister(const IdentifierType& id) {
return associations_.erase(id) == 1;
}
template <typename... Arguments>
AbstractProduct CreateObject(const IdentifierType& id, Arguments&& ... args) const {
auto const &foo = std::get<std::map<IdentifierType, boost::function<AbstractProduct(const Arguments&...)>>>(associations_);
auto const i = foo.find(id);
if (i != foo.end()) {
return (i->second)(std::forward<Arguments...>(args)...);
}
throw std::runtime_error("Creator not found.");
}
private:
AssociativeContainers associations_;
};
struct Arity {
virtual ~Arity() = default;
};
struct Nullary : Arity {};
struct Unary : Arity {
Unary() {}
Unary(double x) : x(x) {}
double x;
};
int main(void)
{
Factory<Arity*, int, Arity*(), Arity*(const double&)> factory;
factory.Register(0, boost::function<Arity*()>{boost::factory<Nullary*>()} );
factory.Register(1, boost::function<Arity*(const double&)>{boost::bind(boost::factory<Unary*>(), _1)});
auto x = factory.CreateObject(1, 2.0);
assert(typeid(*x) == typeid(Unary));
x = factory.CreateObject(0);
assert(typeid(*x) == typeid(Nullary));
}
Hallelujah, I found a solution using Boost.Variant but no type erasure. I think this is much better than my earlier answer, as:
Creator id is unique.
CreateObject supports implicit conversion of parameters to constructor.
The same limitation that the constructors must take const& parameters exists.
I have simplified the overall design somewhat to focus on the essential behaviour. What's missing is the policy for error handling and configurable associative container type, which should be additional class template parameters. I have also left some minimal debugging code in so that you can see for yourself that it works when you test it out.
#include <boost/functional/factory.hpp>
#include <boost/function.hpp>
#include <boost/variant.hpp>
#include <map>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
// Just for debugging.
#include <cassert>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
// Tuple manipulation.
template <typename Signature>
struct signature_impl;
template <typename ReturnType, typename... Args>
struct signature_impl<ReturnType(Args...)>
{
using return_type = ReturnType;
using param_types = std::tuple<Args...>;
};
template <typename T>
using signature_t = signature_impl<T>;
template <std::size_t... Ints>
struct indices {};
template <std::size_t N, std::size_t... Ints>
struct build_indices : build_indices<N-1, N-1, Ints...> {};
template <std::size_t... Ints>
struct build_indices<0, Ints...> : indices<Ints...> {};
template <typename Tuple>
using make_tuple_indices = build_indices<std::tuple_size<typename std::remove_reference<Tuple>::type>::value>;
// The multiple-signature factory.
template <class AbstractProduct, typename IdentifierType, typename... ProductCreators>
class multifactory
{
using functions = boost::variant<boost::function<ProductCreators>...>;
std::map<IdentifierType, functions> associations_;
template <typename Signature>
struct dispatch_foo
{
template <typename CreateArgs, std::size_t... Indices>
typename std::enable_if<std::is_convertible<CreateArgs, typename signature_t<Signature>::param_types>::value, AbstractProduct>::type
static apply(boost::function<Signature> const &f, CreateArgs && t, indices<Indices...>)
{
return f(std::get<Indices>(std::forward<CreateArgs>(t))...);
}
template <typename CreateArgs, std::size_t... Indices>
typename std::enable_if<!std::is_convertible<CreateArgs, typename signature_t<Signature>::param_types>::value, AbstractProduct>::type
static apply(boost::function<Signature> const &, CreateArgs &&, indices<Indices...>)
{
return nullptr;
}
};
template <typename... CreateArguments>
struct dispatcher : boost::static_visitor<AbstractProduct>
{
std::tuple<CreateArguments...> args;
dispatcher(CreateArguments const&... args) : args{std::forward_as_tuple(args...)} {}
template <typename Signature>
AbstractProduct operator()(boost::function<Signature> const &f) const
{
int status;
std::cout << "visitor: " << abi::__cxa_demangle(typeid(Signature).name(), nullptr, 0, &status) << "\n";
return dispatch_foo<Signature>::apply(f, args, make_tuple_indices<std::tuple<CreateArguments...>>{});
}
};
public:
template <typename ProductCreator>
bool Register(IdentifierType id, ProductCreator &&creator) {
return associations_.emplace(id, std::forward<ProductCreator>(creator)).second;
}
bool Unregister(const IdentifierType& id) {
return associations_.erase(id) == 1;
}
template <typename... Arguments>
AbstractProduct CreateObject(const IdentifierType& id, Arguments const& ... args) {
auto i = associations_.find(id);
if (i != associations_.end()) {
dispatcher<Arguments...> impl(args...);
return boost::apply_visitor(impl, i->second);
}
throw std::runtime_error("Creator not found.");
}
};
struct Arity {
virtual ~Arity() = default;
};
struct Nullary : Arity {};
struct Unary : Arity {
Unary() {} // Also has nullary ctor.
Unary(int) {}
};
int main(void)
{
multifactory<Arity*, int, Arity*(), Arity*(const int&)> factory;
factory.Register(0, boost::function<Arity*()>( boost::factory<Nullary*>() ));
factory.Register(1, boost::function<Arity*(const int&)>(boost::factory<Unary*>()) );
auto a = factory.CreateObject(0);
assert(a);
assert(typeid(*a) == typeid(Nullary));
auto b = factory.CreateObject(1, 2);
assert(b);
assert(typeid(*b) == typeid(Unary));
}
I'm trying to extend lexical_cast to handle string->cv::Point conversions, with code like so:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
namespace boost {
template<>
cv::Point2f lexical_cast(const std::string &str) {
std::vector<std::string> parts;
boost::split(parts, str, boost::is_any_of(","));
cv::Point2f R;
R.x = boost::lexical_cast<float>(parts[0]);
R.y = boost::lexical_cast<float>(parts[1]);
return R;
}
}
int main(int argc, char **argv) {
auto p = boost::lexical_cast<cv::Point2f>(std::string("1,2"));
std::cout << "p = " << p << std::endl;
return 0;
}
And it works great.. However, cv::Point2f is actually cv::Point_<T> where T can be int, float, double, etc. I can't find anyway to expose that templated arg to the lexical_cast, so that I can have a single lexical_cast function that can handle all cv::Point_<T> types.
template <typename T>
struct point_type {};
template <typename T>
struct point_type<cv::Point_<T>> { using type = T; };
namespace boost {
template <typename T, typename U = typename point_type<T>::type>
T lexical_cast(const std::string &str)
{
std::vector<std::string> parts;
boost::split(parts, str, boost::is_any_of(","));
T R;
R.x = boost::lexical_cast<U>(parts[0]);
R.y = boost::lexical_cast<U>(parts[1]);
return R;
}
}
DEMO
The previous, a little more complicated solution, if you don't like this implicit second template parameter of lexical_cast:
#include <type_traits>
template <typename T>
struct is_point : std::false_type {};
template <typename T>
struct is_point<cv::Point_<T>> : std::true_type {};
template <typename T>
struct point_type;
template <typename T>
struct point_type<cv::Point_<T>> { using type = T; };
namespace boost {
template <typename T>
auto lexical_cast(const std::string &str)
-> typename std::enable_if<is_point<T>::value, T>::type
{
std::vector<std::string> parts;
boost::split(parts, str, boost::is_any_of(","));
using U = typename point_type<T>::type;
T R;
R.x = boost::lexical_cast<U>(parts[0]);
R.y = boost::lexical_cast<U>(parts[1]);
return R;
}
}
DEMO 2