I want to store an arbitrary number of different classes (all shared-pointered) in some manager-class. The different classes have to be derived from the same CRTP-interface-class. Finally, I want to be able to iterate over all stored classes and calling some functions of the interface. I do not want to create a common base class and I want only use compile-timed stuff.
So I read a few articles in the internet and stole some concepts together. Now I have a working solution (I hope, I am new to templates in C++!), but I think, that is by far too much overkill for such a "simple" requirement.
Can you plz help me to optimize (simplify/shrink/modify) the following minimal example or provide some smarter solution? (no boost and only C++11/14/17)
#include <iostream>
#include <tuple>
#include <memory>
template <class T_IMPLEMENTATION>
struct ISystem {
bool run(int i) { return static_cast<T_IMPLEMENTATION*>(this)->runImpl(i); }
};
struct SystemA : public ISystem<SystemA> {
friend ISystem<SystemA>;
private:
bool runImpl(int i) { std::cout << this << " A:" << i << std::endl; return i; };
};
struct SystemB : public ISystem<SystemB> {
friend ISystem<SystemB>;
private:
bool runImpl(int i) { std::cout << this << " B:" << i << std::endl; return i; };
};
template<typename... ARGS>
struct SystemManager {
template <int index, typename... Ts>
struct runSystem {
void operator()(std::tuple<Ts...>& t, int i) {
std::get<TUPLE_SIZE - index - 1>(t)->run(i++);
runSystem<index - 1, Ts...>{}(t, i);
}
};
template <typename... Ts>
struct runSystem<0, Ts...> {
void operator()(std::tuple<Ts...>& t, int i) {
std::get<TUPLE_SIZE - 1>(t)->run(i);
}
};
template <typename...SPTR_ARGS>
void addSystems(SPTR_ARGS...args) {
m_tupleSystems = std::make_tuple(args...);
}
void run() {
m_value = 0;
runSystem<TUPLE_SIZE - 1, std::shared_ptr<ISystem<ARGS>>...>{}(m_tupleSystems, m_value);
}
private:
using TUPLE_TYPE = std::tuple<std::shared_ptr<ISystem<ARGS>>...>;
static constexpr auto TUPLE_SIZE = std::tuple_size<TUPLE_TYPE>::value;
TUPLE_TYPE m_tupleSystems;
int m_value;
};
int main() {
auto sptrSystemA = std::make_shared<SystemA>();
auto sptrSystemB = std::make_shared<SystemB>();
SystemManager<SystemA, SystemB> oSystemManager;
oSystemManager.addSystems(sptrSystemA, sptrSystemB);
oSystemManager.run();
return 0;
}
Related
I want to create class which should containe map with function pointers (subscribers). But that functions can be with different signature. My code looks like this but it not completed and I am not sure if that is right. Can somebody help me please how to correct append pointers to map and invoke them in myMainClass::start()?
myMainClass.h
#pragma once
#include "iostream";
#include "mySubscriber.h"
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
}
// SHOULD BE SOMETHING LIKE THIS
bool start() {
mySubscriber ss;
ss.subscribe("callback1", callback1);
ss.subscribe("callback2", callback2);
ss.getSubscribe("callback1")(5);
ss.getSubscribe("callback2")("test");
}
};
mySubscriber.h
#pragma once
#include "map";
#include "string";
#include "functional";
class mySubscriber {
typedef std::function<void()> func;
std::map<std::string, func*> _subscribes;
public:
mySubscriber() : _subscribes{} {}
~mySubscriber() {
_subscribes.clear();
}
/*
* append or change function pointer
*/
void subscribe(std::string fName, func* f) {
auto find = _subscribes.find(fName);
if (find != _subscribes.end())
{
find->second = f;
}
else
{
_subscribes.emplace(fName, f);
}
}
/*
* get subscribe function
*/
func* getSubscribe(std::string fName) {
auto find = _subscribes.find(fName);
if (find != _subscribes.end())
{
return find->second;
}
return NULL;
}
};
At first some general hints:
Avoid raw pointer usage as far as possible, even for internals! Use std::unique_ptr or std::shared_ptr instead!
Reducing a data conglomerate to a standard container, indexing it via a dynamic data type like std::string and use it in a plain void std::function context results (almost?) always in type erasure and a loss of according type safe outer access. In fact, this even has nothing to do with further differences between plain functions and member methods in the first place.
A possible first solution approach:
This is a minimal working example that should cover your quite dynamic requirements. For me it compiles and runs well with MS VS 2017 (C++17). I tried to use your original structs as far as possible.
#include <variant>
#include <set>
#include <string>
#include <iostream>
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
return true;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
return true;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
return true;
}
template <typename T> class CallbackBaseTmpl;
template <typename Ret, typename ...Args>
class CallbackBaseTmpl<Ret(Args...)>
{
public:
using Signature = Ret(Args...);
CallbackBaseTmpl(const std::function<Signature>& func) : m_function(func) {}
CallbackBaseTmpl(std::function<Signature>&& func) :
m_function(std::move(func)) {}
inline Ret Func(Args&&... args) { return m_function(std::forward<Args>(args)...); }
private:
std::function<Signature> m_function;
};
class Callback1Type : public CallbackBaseTmpl<bool(int)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
class Callback2Type : public CallbackBaseTmpl<bool(std::string)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
class Callback3Type : public CallbackBaseTmpl<bool(int, std::string)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
using CompoundCallbackType = std::variant<Callback1Type, Callback2Type, Callback3Type>;
class CallbackHolder
{
public:
CallbackHolder(const CompoundCallbackType& callbackImpl) : m_callbacksImpl(callbackImpl) {}
inline auto getIndex() const { return m_callbacksImpl.index(); }
inline CompoundCallbackType& getImpl() const { return m_callbacksImpl; }
private:
mutable CompoundCallbackType m_callbacksImpl;
};
class CallbacksContainer
{
public:
template <typename VariantType>
bool subscribe(const VariantType& compoundType)
{
return subscribe(CallbackHolder(compoundType));
}
bool subscribe(const CallbackHolder& cHolder)
{
auto res = m_containerImpl.insert(cHolder);
return res.second;
}
template <typename CallbackType, typename... Args>
auto getSubscribe(Args&&... args)
{
// linear search - can be optimized
for (auto& implEntry : m_containerImpl)
{
bool isWanted = std::visit([&args...](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, CallbackType>)
return true;
else
return false;
}, implEntry.getImpl());
if (isWanted)
return std::get<CallbackType>(implEntry.getImpl()).Func(std::forward<Args>(args)...);
}
throw std::logic_error("Cannot access element");
}
private:
struct CustomComparer {
bool operator() (const CallbackHolder& lhs, const CallbackHolder& rhs) const
{
// Each variant entry allowed only once in the set
return lhs.getIndex() < rhs.getIndex();
}
};
std::set<CallbackHolder, CustomComparer> m_containerImpl;
};
bool start() {
CallbacksContainer ms;
ms.subscribe(Callback1Type(std::bind(&myMainClass::callback1, this, std::placeholders::_1)));
ms.subscribe(Callback2Type(std::bind(&myMainClass::callback2, this, std::placeholders::_1)));
ms.getSubscribe<Callback1Type>(5);
ms.getSubscribe<Callback2Type>("TEST");
ms.subscribe(Callback3Type(std::bind(&myMainClass::callback3, this, std::placeholders::_1, std::placeholders::_2)));
ms.getSubscribe<Callback3Type>(2, "");
return true;
}
};
Explanation: I replaced your original map with an std::set as a kind of registry container so there are still no duplicates allowed. Some efforts are required via Wrappers to achieve the desired final access scheme.
You can easily change the desired registered functions for a type in a dynamic but always very type safe way now. Feel free to extend this scheme for your own purposes. Likely, there are several parts that can be optimized, shortened or extended. Maybe there's also a nice way to avoid this mutable inside the CallbackHolder. The (non-grave for a few functions) linear search within the set can be avoided via an actual typeid sorting and specialized according finding for instance.
Update due to feedback:
If strings as keys are required and a maximum degree of freedom should be given, i.e. any callback type should be providable without the necessity for compile time registration, this solution might be an alternative:
#include <map>
#include <string>
#include <iostream>
#include <functional>
#include <memory>
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
return true;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
return true;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
return true;
}
class ICallback
{
public:
virtual ~ICallback() = default;
};
template <typename T> class TypedCallback;
template <typename Ret, typename ...Args>
class TypedCallback<Ret(Args...)> : public ICallback
{
public:
using Signature = Ret(Args...);
TypedCallback(const std::function<Signature>& func) : m_function(func) {}
TypedCallback(std::function<Signature>&& func) :
m_function(std::move(func)) {}
inline Ret Func(Args&&... args) { return m_function(std::forward<Args>(args)...); }
private:
std::function<Signature> m_function;
};
class CallbacksContainer
{
private:
template <typename T> struct CallTraits {};
template <typename C, typename Ret, typename... Args>
struct CallTraits<Ret(C::*)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename C, typename Ret, typename... Args>
struct CallTraits<Ret(C::*)(Args...) const>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename F>
struct FuncTraits
{
using FuncClass = std::decay_t<F>;
using OperatorSignature = decltype(&FuncClass::operator());
using signature = typename CallTraits<OperatorSignature>::Signature;
using returnType = typename CallTraits<OperatorSignature>::ReturnType;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(*)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(&)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
public:
template <typename T>
bool subscribe(const std::string& key, T&& func)
{
auto res = m_subscriptions.try_emplace(
key, std::make_unique<TypedCallback<typename FuncTraits<T>::signature>>(std::forward<T>(func)));
return res.second;
}
template <typename Ret, typename... Args>
auto getSubscribe(const std::string& key, Args&&... args) const
{
using Signature = Ret(Args...);
const auto& entry = m_subscriptions.at(key);
auto rp = entry.get();
auto typedCB = dynamic_cast<TypedCallback<Signature>*>(rp);
if (typedCB == nullptr)
{
// TODO: Possible further check if functor can be used due to convertible types, for instance
// with an acyclic visitor?
std::logic_error("Wrong callback signature provided.");
}
return typedCB->Func(std::forward<Args>(args)...);
}
private:
std::map<std::string, std::unique_ptr<ICallback>> m_subscriptions;
};
bool start() {
CallbacksContainer ms;
// Usage with non static member methods
ms.subscribe("callback1", [this](int x) { return callback1(x); });
ms.subscribe("callback2", [this](std::string x) { return callback2(x); });
ms.subscribe("callback3", [this](int x, std::string str) { return callback3(x, str); });
// Usage with lambda
ms.subscribe("callback4", [](int y) { return y != 0; });
// Usage with std::function itself
ms.subscribe("callback5", std::function<bool(int)>([](int y) { return y != 0; }));
// Getters - Unfortunately, exact types are required. Maybe acyclic visitor could help here?
ms.getSubscribe<bool>("callback1", 1);
ms.getSubscribe<bool>("callback2", std::string("TEST"));
ms.getSubscribe<bool>("callback3", 1, std::string("TEST"));
ms.getSubscribe<bool>("callback4", 1);
return true;
}
};
PROs:
No static/compile time method signature registration required -> no variants
At least with C++20, method subscription will be an easy going here, added some helper traits to make things a bit easier here already
Only one underlying map used
CONs:
Less type-safety at some points and the dynamic_cast might be a bit slow but might be improved in terms of performance via a simple type index comparison
The getSubscribe() method has to be used with care. Exact types are required here (the former dynamically registered ones) and it doesn't unfortunately support common signature conversion ways. I see currently no way to get rid of this problem with pre C++20 features. Maybe some tricks with a generic acyclic visitor pattern or SFINAE magic + visitor might help here but that breaks the mould by far here I think. If that arises as a real issue, one can still use the chained parameter scheme in doubt, that guarantees type safety on its own.
You have to somehow turn the memberfunction pointers to regular old function pointers, in order to store them in the same container. You have three options that I can come up with:
#include <functional>
struct Foo {
void foo(int x, int y, int z) {}
/*
Putting the instance as the first parameter is crucial, because the
first argument to a member function call is an implicit this. If instance
is not the first parameter the compiler has to shift around the argument
list, otherwise it's a direct forwarding call.
*/
static void callback(void* instance, int x, int y, int z) {
return static_cast<Foo*>(instance)->foo(x, y, z);
}
};
int main() {
Foo foo;
void (*f0)(void*, int, int, int){&Foo::callback};
/*
Capturing lambda cannot decay to function pointer, have to use
std::function or smth. similar
*/
std::function<void(int, int, int)> f1{
[&](int x, int y, int z) { return foo.foo(x, y, z); }};
auto f2 = std::mem_fn(&Foo::foo);
f0(&foo, 1, 2, 3);
f1(1, 2, 3);
f2(&foo, 1, 2, 3);
}
Here's a godbolt with the generated assembly https://godbolt.org/z/K9eM4E
I have a template class and a member function print() to print the data.
template<typename T>
class A
{
public:
T data;
void print(void)
{
std::cout << data << std::endl;
}
// other functions ...
};
Then, I want to either print scalar data or vector data, so I give a specialized definition and get a compiler error.
template<typename T>
void A<std::vector<T>>::print(void) // template argument list error
{
for (const auto& d : data)
{
std::cout << d << std::endl;
}
}
Question: Why does this member function specialization get an error? What is the correct way to define a print function for a vector?
Solution 1: I have tested the following definition.
template<typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print(void) { // OK
// ...
}
}
This one worked, but I have to copy the other member functions into this specialized class.
EDIT:
Solution 2: To prevent copy all the other member functions, I define a base class containing the common member functions and inherit from the base class:
template<typename T>
class Base
{
public:
T data;
// other functions ...
};
template<typename T>
class A : public Base<T>
{
public:
void print(void)
{
std::cout << this->data << std::endl;
}
};
template<typename T>
class A<std::vector<T>> : public Base<std::vector<T>>
{
public:
void print(void)
{
for (const auto& d : this->data)
{
std::cout << d << std::endl;
}
}
};
This solution works well. Are there some better or more conventional solutions?
Why does this member function specialization get error?
When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.
Are there some better solutions?
Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};
(See Live Online)
This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.
If you don not have access to C++17, other option is to SFINAE the members(Since c++11).
#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}
template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};
(See Live Online)
What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?
You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// custom MyVector (An example)
template<typename T> struct MyVector {};
template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};
template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};
And the print function could be in c++17:
void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}
(See Live Online)
You need to implement a template class that uses a vector as template parameter. This worked for me.
template<typename T>
class A
{
public:
T data;
void print(void) {
std::cout << "Data output" << std::endl;
}
// other functions ...
};
template <typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print() {
for (auto i : data) {
std::cout << "Vector output" << std::endl;
}
}
};
You could always use named tag dispatching to check if type provided by template user is vector.
A<std::vector<T>> notation won't work as you both try to take into account that T is type and vector of types which is contradicting with itself.
Below is code I used named tag dispatching as solution to your problem:
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template<typename T>
class A
{
public:
T data;
void print(std::true_type) {
for (auto& a : data) { std::cout << a << std::endl; }
}
void print(std::false_type) {
std::cout << data << std::endl;
}
void print() {
print(is_vector<T>{});
}
};
int main()
{
A<int> a;
a.data = 1;
a.print();
A<std::vector<int>> b;
b.data = { 1, 2 ,3 ,4 ,5 };
b.print();
return 0;
}
Succesfully compiled with https://www.onlinegdb.com/online_c++_compiler
Based on answer: Check at compile-time is a template type a vector
You can dispatch printing to another member function (static or not). For example:
template<typename T>
class A {
public:
T data;
void print() const {
print_impl(data);
}
private:
template<class S>
static void print_impl(const S& data) {
std::cout << data;
}
template<class S, class A>
static void print_impl(const std::vector<S, A>& data) {
for (const auto& d : data)
std::cout << d;
}
};
I have the following code where I try to detect what concept(member functions in this case) alternative in variant holds.
It is verbose and relatively ugly.
Is there a nicer way to do this?
Note that I do not want to use inheritance, and I do not want to use static polymorphism(let's assume what engine is used is not known at compile time).
In simple terms I am looking for runtime mix of if constexpr(that only checks concepts at compile time) and std::holds_alternative (that can only check for if specific type is in variant, not if any of the types satisfying the concept is in variant).
#include <iostream>
#include <variant>
struct simple_engine1{
};
struct simple_engine2{
};
struct complex_engine1{
void reduce_thrust(int perc){
std::cout<<"reducing thrust " << perc << "% " << std::endl;
}
};
struct complex_engine2{
void reduce_thrust(int perc){
std::cout<<"reducing thrust " << perc << "% " << std::endl;
}
};
template< class, class = std::void_t<> >
struct has_reduce_thrust : std::false_type { };
template< class T >
struct has_reduce_thrust<T,
std::void_t<decltype( std::declval<T>().reduce_thrust(42) )>
> : std::true_type { };
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(has_reduce_thrust<complex_engine1>::value);
struct visitor{
template<typename T>
void operator()(T& t){
dispatch(t, has_reduce_thrust<T>{});
}
template<typename T>
void dispatch(T& t, std::true_type /*has_reduce_thrust*/){
t.reduce_thrust(perc);
reduced_thrust=true;
}
template<typename T>
void dispatch(T& , std::false_type){
reduced_thrust=false;
}
int perc = 0;
bool reduced_thrust = false;
};
// tries to reduce speed by reducing thrust if engine supports it, if not
// it fires reverse engines(more expensive wrt fuel usage)
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2>* var_engine){
visitor v;
v.perc = 47;
std::visit(v, *var_engine);
if (v.reduced_thrust) {
std::cout << "reduced thrust\n";
} else {
std::cout << "activating reverse engines\n";
}
}
int main() {
std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2> var_engine{simple_engine1{}};
reduce_speed(&var_engine);
var_engine = complex_engine2{};
reduce_speed(&var_engine);
var_engine = simple_engine2{};
reduce_speed(&var_engine);
var_engine = complex_engine2{};
reduce_speed(&var_engine);
}
You can simplify the visitor a lot by using if constexpr:
struct visitor{
template<typename T>
void operator()(T& t) {
if constexpr (has_reduce_thrust<T>::value) {
t.reduce_thrust(perc);
reduced_thrust = true;
}
else {
reduced_thrust = false;
}
}
int perc = 0;
bool reduced_thrust = false;
};
You could then abstract further by accepting any predicate and two functions for either branch of the if constexpr:
template <template <class, class... /*SFINAE friendly*/> class TypePred,
class MatchedFunc, class UnmatchedFunc>
class predicated_visitor {
public:
predicated_visitor(MatchedFunc matchedFunc, UnmatchedFunc unmatchedFunc)
: _matchedFunc(matchedFunc), _unmatchedFunc(unmatchedFunc) {}
template <typename T>
void operator()(T& t) {
if constexpr (TypePred<T>::value)
_matchedFunc(t);
else
_unmatchedFunc(t);
}
private:
MatchedFunc _matchedFunc;
UnmatchedFunc _unmatchedFunc;
};
template <template <class, class... /*SFINAE friendly*/> class TypePred,
class F1, class F2>
auto makePredicatedVisitor(F1 f1, F2 f2) {
return predicated_visitor<TypePred, F1, F2>(f1, f2);
}
The resulting code is quite nice I feel:
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1,
complex_engine2>* var_engine) {
int perc = 47;
bool reducedThrust = false;
auto reduceableThrustAction = [perc, &reducedThrust](auto& t) {
t.reduce_thrust(perc);
reducedThrust = true;
};
auto alternativeAction = [](auto& t) {
}; // Could explicitly set reduceThrust to false for clarity.
auto thrust_visitor = makePredicatedVisitor<has_reduce_thrust>(
reduceableThrustAction, alternativeAction);
std::visit(thrust_visitor, *var_engine);
if (reducedThrust) {
std::cout << "reduced thrust\n";
} else {
std::cout << "activating reverse engines\n";
}
}
Demo
This example compiles to essentially the same assembly code as yours but can be reused in any way you like.
I apologize for the inconsistent capitalization...
I would like to do something like this:
#include <iostream>
class a {
public:
a() : i(2) {}
template <typename ...ts>
void exec() {
f<ts...>();
std::cout << "a::()" << std::endl;
}
int i;
private:
template <typename t>
void f() {
i += t::i;
}
template <typename t, typename ...ts>
void f() {
f<t>();
f<t, ts...>();
}
};
struct b {
static const int i = -9;
};
struct c {
static const int i = 4;
};
int main()
{
a _a;
_a.exec<b,c>();
std::cout << _a.i << std::endl;
}
The idea is to get the same information from a group of classes, without the need of an object of each class.
Does anyone know if it is possible?
Thanks!
In case Your compiler does not support C++17:
template <typename ...ts>
void f() {
for ( const auto &j : { ts::i... } )
i += j;
}
In C++17, your class would simply be
class a {
public:
a() : i(2) {}
template <typename ...ts>
void exec() {
((i += ts::i), ...); // Folding expression // C++17
std::cout << "a::()" << std::endl;
}
int i;
};
Possible in C++11 too, but more verbose.
Reasons why your code is not compiling:
Syntax of specializing templates is a little different.
You need to put the most general case first.
You can't partially specialize functions, only classes.
Partial specialization is not allowed within classes, only in namespaces.
Here is an example for C++11.
#include <iostream>
template<typename t, typename ...ts>
class a {
public:
static constexpr int x = t::i + a<ts...>::x;
};
template<typename t>
class a<t> {
public:
static constexpr int x = 2 + t::i;
};
struct b {
static constexpr int i = -9;
};
struct c {
static constexpr int i = 4;
};
int main()
{
constexpr int result = a<b,c>::x;
std::cout << result << std::endl;
}
Remember that templates are calculated during compilation so, for optimization sake, it is a good idea to write them in a way that allows them to be constexpr.
I am new to SFINAE. I have a template that I would like to be able to accept classes that the size could be determined simply calling sizeof(x) or in case the value is dynamic it will require x.size().
I am trying to wrap my head around how as smooth as possible this could looks like and I think interface: size_t size(const Item& item) seems to be good enough.
The following is an example that works:
#include <iostream>
#include <cstdio>
#include <type_traits>
template <typename T>
class Fixed {
public:
typedef T Item;
static const bool kFixedSize = true;
static size_t size() {
return sizeof(T);
}
};
template <typename T>
class Dynamic {
public:
typedef T Item;
static const bool kFixedSize = false;
static size_t size(const T& item) {
return item.size();
}
};
template <typename T>
class Serialize {
public:
template <typename = typename std::enable_if<T::kFixedSize> >
size_t size(typename T::Item&) {
return T::size();
}
template <typename = typename std::enable_if<!T::kFixedSize> >
size_t size(const typename T::Item& item) {
return T::size(item);
}
};
int main() {
Serialize< Fixed<int> > fixed;
int a = 0;
std::cout << fixed.size(a) << std::endl;
Serialize< Dynamic<std::string> > dynamic;
std::cout << dynamic.size("string") << std::endl;
return 0;
}
It has an issues though one is: size_t size(typename T::Item&) and the other is size_t size(const typename T::Item& item) else the compiler compliance that I am overloading the template. The second is it seems like too match very tricky code to achieve the goal - is there better ways to do this?
I believe you want something like this
//class hierarchy to set the priority for type matching
struct second_priority
{
};
struct first_priority : public second_priority
{};
template<typename T>
auto size_impl(T const & data, second_priority t) -> int
{
return sizeof(data);
}
template<typename T>
auto size_impl(T const & data , first_priority t) -> decltype(data.size(),int())
{
return data.size();
}
template<typename T>
int size(T const & data )
{
return size_impl(data,first_priority{});
}
I think #Gautam Jha presented a nice solution using SFINAE. You can shorten it a bit by using ellipsis for the 'else' case, so you don't need to use this auxiliary class and it's inheritance:
template<typename T>
auto size_impl(T const & item, int) -> decltype(item.size())
{
return item.size();
}
template<typename T>
auto size_impl(T const & item, ...) -> size_t
{
return sizeof(T);
}
template<typename T>
auto size(T const & item) -> size_t
{
return size_impl(item, 0);
}
It's cool that you're playing around with SFINAE, but usually there are simpler (i.e. to read and to understand) ways to achieve the same, see the solution of POW (which has unfortunately been deleted).
Since all you want to do is call different functions to get the size in Dynamic or Fixed, you can just implement these classes differently and use them in Serialize:
#include <iostream>
#include <cstdio>
#include <type_traits>
template <typename T>
class Fixed {
public:
typedef T Item;
static size_t size(const T&) {
return sizeof(T);
}
};
template <typename T>
class Dynamic {
public:
typedef T Item;
static size_t size(const T& item) {
return item.size();
}
};
template <typename T>
class Serialize {
public:
size_t size(typename T::Item const& x) {
return T::size(x);
}
};
int main() {
Serialize< Fixed<int> > fixed;
int a = 0;
std::cout << fixed.size(a) << std::endl;
Serialize< Dynamic<std::string> > dynamic;
std::cout << dynamic.size( std::string{"string"} ) << std::endl;
return 0;
}
However, I would consider using a type-trait or a free function to do the same. This would be more extensible, because you have to just provide a new trait or an overload for new types, e.g. some container which has only a length method.
#include <iostream>
#include <cstdio>
#include <type_traits>
size_t size(int) {return sizeof(int);}
size_t size(std::string const& s) {return s.size();}
template<typename T>
struct size_trait
{
};
template<>
struct size_trait<int>
{
static size_t size(int) {return sizeof(int);}
};
template<>
struct size_trait<std::string>
{
static size_t size(std::string const& x) {return x.size();}
};
template <typename T>
class Serialize {
public:
size_t size(T const& x) {
return ::size(x);
}
size_t size_(T const& x) {
return size_trait<T>::size(x);
}
};
int main() {
Serialize< int > fixed;
int a = 0;
std::cout << fixed.size(a) << std::endl;
std::cout << fixed.size_(a) << std::endl;
Serialize< std::string > dynamic;
std::cout << dynamic.size( std::string{"string"} ) << std::endl;
std::cout << dynamic.size_( std::string{"string"} ) << std::endl;
return 0;
}