I am currently learning boost::hana for a personal project.
In the following code snippet I created a boost::hana::map having a type_c<T> as key and an instance of Foo<T> as value.
It works but I would really like to use the my_map variable as a class member and it is not possible to use auto qualifier in member declarations. Moreover it would be great if I could have the possibility to pass the types tuple in some way (as a template parameter or as a constructor parameter).
Do you have any suggestions?
#include <iostream>
#include "boost/hana.hpp"
#include <typeinfo>
using namespace boost;
template<typename T>
class Foo {
T t;
public:
void print() { std::cout <<typeid(T).name() << t; }
};
int main() {
auto types = hana::tuple_t<float, int, std::string>;
auto my_map = boost::hana::unpack(types, [](auto ...t) {
return boost::hana::make_map(boost::hana::make_pair(t, Foo<typename decltype(t)::type>()) ...);
});
my_map[hana::type_c<int>].print();
}
The problem is that lambdas cannot be used in un-evaluated context (decltype).
Of course, since c++14 we can just use deduced return types on any free function:
namespace detail {
template <typename... T>
static inline auto make_foo_map() {
return boost::hana::unpack(hana::tuple_t<T...>, [](auto... t) {
return boost::hana::make_map(boost::hana::make_pair(t, Foo<typename decltype(t)::type>())...);
});
}
}
template <typename... T>
using FooMap = decltype(detail::make_foo_map<T...>());
Now it's simply:
FooMap<float, int, std::string> my_map;
Live Demo
Live On Coliru
#include "boost/hana.hpp"
#include <iostream>
#include <typeinfo>
using namespace boost;
template <typename T> class Foo {
T t;
public:
void print() { std::cout << typeid(T).name() << "t\n"; }
};
namespace detail {
template <typename... T>
static inline auto make_foo_map() {
return boost::hana::unpack(hana::tuple_t<T...>, [](auto... t) {
return boost::hana::make_map(boost::hana::make_pair(t, Foo<typename decltype(t)::type>())...);
});
}
}
template <typename... T>
using FooMap = decltype(detail::make_foo_map<T...>());
int main() {
FooMap<float, int, std::string> my_map;
my_map[hana::type_c<int>].print();
my_map[hana::type_c<float>].print();
my_map[hana::type_c<std::string>].print();
}
Related
I have a template class, with an internal method that is itself templated.
Consider the following minimal working example
#include <functional>
template<typename T>
class P{
public:
template <typename U>
P<U> call(std::function< P<U> (T)> f){
return f(return_value);
}
T return_value;
};
P<int> foo() {
P<int> value = P<int>();
return value;
}
P<float> bar(int arg) {
return P<float>();
}
int main()
{
auto res = foo().call<float>(bar);
return 0;
}
As you can see in the main function, the compiler forces me to to specify the float type for calling the call() function, eventhough the type should be obvious from passing over the bar() function, that specifies the float type.
Is it somehow possible to deduct the type so that the code in the main function can be simplified to the following statement:
auto res = foo().call(bar);
std::function is not the type that you should use whenever you want to pass a function around. std::function is a type that uses type erasure to be able to store different types of callables in one type. If you don't need that then you need no std::function:
#include <functional>
template<typename T>
class P{
public:
template <typename F>
auto call(F f){
return f(return_value);
}
T return_value{}; // don't forget to initialize !
};
P<int> foo() {
P<int> value = P<int>();
return value;
}
P<float> bar(int arg) {
return P<float>();
}
int main()
{
auto res = foo().call(bar);
return 0;
}
Using partial specializatzion you can get the return type of bar and you can get the float from P<float>:
#include <type_traits>
#include <iostream>
template <typename T> class P;
// get return type from function
template <typename T> struct return_type;
template <typename R,typename...args>
struct return_type<R(args...)> { using type = R; };
// get T from P<T>
template <typename P> struct P_arg;
template <typename T> struct P_arg< P<T> > { using type = T; };
// combine both
template <typename F>
struct get {
using type = typename P_arg<typename return_type<F>::type >::type;
};
template<typename T>
class P{
public:
template <typename F>
auto call(F f){
return f(return_value);
}
T return_value{};
};
P<float> bar(int arg) {
return P<float>();
}
int main()
{
std::cout << std::is_same_v< get<decltype(bar)>::type,float>;
return 0;
}
Though that does not really help here, because you cannot use it to decalre the return type of P::call, as it requires P<float> to be already complete.
I use std::experimental::is_detected to determine if class has certain member functions:
#include <utility>
#include <experimental/type_traits>
template<typename USC>
class Descriptor
{
private:
template<class T>
using has_member1_t =
decltype(std::declval<T>().member1(std::declval<std::vector<char> &>()));
public:
static constexpr bool has_member1 =
std::experimental::is_detected_convertible_v<long long,
has_member1_t,
USC>;
};
The problem is I also need to determine if class has certain template member function with following signature:
template<typename Derived>
int member2(Eigen::ArrayBase<Derived> &&)
I've tried to do it like so:
//Inside Descriptor
template<class T, class U>
using has_member2_t =
decltype(std::declval<T>().member2(std::declval<Eigen::ArrayBase<U> &&>()));
static constexpr bool has_member2 =
std::experimental::is_detected_convertible_v<long long,
has_member2_t,
USC>;
but it doesn't work. As I'm not really experienced with C++ TMP I would like to know is there a way to achieve this with std::experimental::is_detected or some other utility?
If c++20 is an option, this kind of code has been made a lot easier to both read and write by using concepts.
#include <iostream>
#include <vector>
struct Foo {
int member1(int) {
return 1;
}
template <typename T>
double member2(std::vector<T>) {
return 2.5;
}
};
struct Bar {};
template <typename T>
concept MyConcept = requires (T t) {
{ t.member1(0) } -> std::same_as<int>; // We can check return type
{ t.template member2<int>(std::vector<int>{}) }; // but we don't have to
};
int main() {
static_assert(MyConcept<Foo>);
static_assert(!MyConcept<Bar>);
}
The issue with your attempt is that you are not passing anything as U for the check. I would also modify it to use the .template member2<...> syntax to explicitly check for a template.
Here is an example of that.
#include <iostream>
#include <vector>
#include <utility>
#include <experimental/type_traits>
struct Foo {
int member1(int) {
return 1;
}
template <typename T>
double member2(std::vector<T>) {
return 2.5;
}
};
struct Bar {};
template<class T, class U>
using has_member2_t =
decltype(std::declval<T>().template member2<U>(std::declval<std::vector<U> &&>()));
int main() {
static constexpr bool has_member2_Foo =
std::experimental::is_detected_convertible_v<long long,
has_member2_t,
Foo, double>;
static constexpr bool has_member2_Bar =
std::experimental::is_detected_convertible_v<long long,
has_member2_t,
Bar, double>;
static_assert(has_member2_Foo);
static_assert(!has_member2_Bar);
}
I'm trying to write a logic, with usage of command and factory patterns. Based on a data inside the std::variant I want to create an object. It may look like a map of C++ types.
Here is the code: Wandbox
I can hardcode all used types but I want to automate it with variadic templates, how to do it?
#include <cassert>
#include <variant>
#include <type_traits>
#include <memory>
#include <iostream>
struct IFace
{
virtual void foo() = 0;
};
template<typename TKey, typename TValue>
struct Base : IFace
{
using Key = TKey;
using Value = TValue;
};
struct Int : Base<int, Int>
{
void foo() override { std::cout << "Int"; }
};
struct Double : Base<double, Double>
{
void foo() override { std::cout << "Double"; }
};
using Var = std::variant<int, double>;
template<typename ...Args>
std::shared_ptr<IFace> factory(const Var& v)
{
std::shared_ptr<IFace> p;
std::visit([&p](auto&& arg){
using TKey = std::decay_t<decltype(arg)>;
// TODO: use variadic instead of hardcoded types
if constexpr (std::is_same_v<TKey, int>)
{
using TValue = typename Base<TKey, Int>::Value;
p = std::make_shared<TValue>();
std::cout << "int ";
}
else if constexpr (std::is_same_v<TKey, double>)
{
using TValue = typename Base<TKey, Double>::Value;
p = std::make_shared<TValue>();
std::cout << "double ";
}
}, v);
return p;
}
int main()
{
const Var v = 42;
auto p = factory<Int, Double>(v);
assert(p != nullptr);
p->foo();
return 0;
}
This popular little "overloaded" class is always useful for passing lambdas to std::visit:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
Then, instead of using a generic lambda and trying to deduce the right object type from the parameter type, just generate an overload for each Args.
template<typename ...Args>
std::shared_ptr<IFace> factory(const std::variant<typename Args::Key...>& v)
{
return std::visit(overloaded {
[](const typename Args::Key&) -> std::shared_ptr<IFace> { return std::make_shared<Args>(); }...
}, v);
}
Demo: https://godbolt.org/z/nKGf3c
I want to find all objects of given types and add them to vector.
For now I have code:
template<class T>
void fill1(std::vector<Character*> &vec2)
{
for (int i = 0; i < GameObject::allObjects.size(); i++)
{
if (dynamic_cast<T>(GameObject::allObjects[i]))
{
vec2.push_back(dynamic_cast<Character*>(GameObject::allObjects[i]));
}
}
}
template<class First, class ...T>
void fill2(std::vector<Character*> &vec2)
{
fill1<First>(vec2);
fill2<T...>(vec2);
}
template<class ... T>
std::vector<Character*> SpecialList<T...>::get()
{
std::vector<Character*> vec2;
fill2<T...>(vec2);
return vec2;
}
The code doesn't compile at all.
The error we are getting is:
could not deduce template argument for 'First'
I know that all the given types are inherited from class Character and I have a vector of all my objects (GameObject::allObjects).
Instead of recursion use parameter pack expansion inside list initialization of a dummy array e.g.:
#include <vector>
#include <memory>
#include <tuple>
#include <iostream>
#include <algorithm>
template <class... Ts>
struct tag { };
template <class T>
struct Predicate {
template <class U>
bool operator()(std::shared_ptr<U> sp) const {
return std::dynamic_pointer_cast<T>(sp) != nullptr;
}
};
template <class... Ts, class T>
std::vector<std::shared_ptr<T>> all(std::vector<std::shared_ptr<T>> &v, tag<Ts...>) {
std::vector<std::shared_ptr<T>> result;
int dummy[] {(std::copy_if(v.begin(), v.end(), std::back_inserter(result), Predicate<Ts>{}),0)...};
static_cast<void>(dummy);
return result;
}
struct A {
virtual ~A() = default;
};
struct B: A { };
struct C: A { };
int main() {
std::vector<std::shared_ptr<A>> v { std::make_shared<A>(),
std::make_shared<A>(),
std::make_shared<B>(),
std::make_shared<B>(),
std::make_shared<C>(),
std::make_shared<C>() };
std::cout << all(v, tag<B, C>{}).size() << std::endl;
}
[live demo]
I am trying to create a concept for use with boost::any. This concept should say that
a class has ha member function with signatur void templateFunction(T t). I have gotten this to compile and working fine, but only for one type at a time. Is what I am trying to do impossible?
#include <iostream>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/any_cast.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/operators.hpp>
#include <boost/type_erasure/member.hpp>
#include <boost/type_erasure/free.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/any.hpp>
using namespace std;
namespace mpl = boost::mpl;
using namespace boost::type_erasure;
class Foo
{
public:
template <class T>
void templateFunction(T t)
{
cout << t << endl;
}
};
template<class C, class T>
struct has_template_function
{
static void apply(C& cont, const T& arg) { cont.templateFunction(arg); }
};
namespace boost
{
namespace type_erasure
{
template<class C, class T, class Base>
struct concept_interface<has_template_function<C, T>, Base, C> : Base
{
void templateFunction(typename as_param<Base, const T&>::type arg)
{ call(has_template_function<C, T>(), *this, arg); }
};
}
}
int main()
{
any<has_template_function<_self, int>, _self&> c = Foo();
c.templateFunction(5);
//Compile error: cannot convert parameter 1 from 'const char [6]' to 'const int &'
//c.templateFunction("Hello");
return 0;
}
This is kind of possible by overloading, and documented in the official Boost.TypeErasure documentation.
The caveat is, as said in the comments:
You can't type-erase templates and keep their polymorphic nature
Therefore, you will have to specify the overloads explicitly in the requirements for your boost::typeerasure::any type.
You need to modify your concept interface as described in the docs, and add a string overload to the requirements section.
Your example, modified to handle overloads:
#include <iostream>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/any_cast.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/operators.hpp>
#include <boost/type_erasure/member.hpp>
#include <boost/type_erasure/free.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/any.hpp>
#include <string>
#include <utility>
using namespace std;
namespace mpl = boost::mpl;
using namespace boost::type_erasure;
struct FooStruct
{
template <class T>
void templateFunction(T t)
{
cout << t << endl;
}
};
template<class T, class U>
struct has_template_function
{
static void apply(T& t, const U& u) { t.templateFunction(u); }
};
namespace boost {
namespace type_erasure {
template<class T, class U, class Base, class Enable>
struct concept_interface< ::has_template_function<T, U>, Base, T, Enable> : Base
{
typedef void _fun_defined;
void templateFunction(typename as_param<Base, const U&>::type arg)
{
call(::has_template_function<T, U>(), *this, arg);
}
};
template<class T, class U, class Base>
struct concept_interface< ::has_template_function<T, U>, Base, T, typename Base::_fun_defined> : Base
{
using Base::templateFunction;
void templateFunction(typename as_param<Base, const U&>::type arg)
{
call(::has_template_function<T, U>(), *this, arg);
}
};
}
}
ostream& operator<<(ostream& os, const std::pair<int, string>& pair) {
os << "(" << pair.first << ", " << pair.second << ")";
return os;
}
int main()
{
any<
mpl::vector
<
has_template_function<_self, int>,
has_template_function<_self, std::string>,
has_template_function<_self, std::pair<int,std::string>>
>
, _self&> c = FooStruct();
c.templateFunction(5);
c.templateFunction("Hello");
c.templateFunction(std::make_pair(5, "Hello"));
return 0;
}