Reputation:
I got some classes like
class A {
public:
void OnStageOne() {}
void OnStageTwo() {}
void OnStageThree() {}
};
class B {
public:
void OnStageOne() {}
void OnStageTwo() {}
void OnStageThree() {}
};
class C {
public:
void OnStageOne() {}
void OnStageTwo() {}
void OnStageThree() {}
};
and I got some vectors to store them:
std::vector<A> va;
std::vector<B> vb;
std::vector<C> vc;
now I need some operations like this: call OnStageXXX
on all elements of a vector, so I wrote a template function like this:
template <typename T, void(T::*F)()>
void ForAll(std::vector<T> vector) {
for (auto& item : vector) {
(item.*F)();
}
}
It works fine, except that I have to use syntax like this:
ForAll<A, &A::OnStageOne>(va);
Since the first template parameter can be deduced for the parameter va
, is there any way I can make the code more cleaner? Like ForAll<OnStageOne>(va);
?
Or, if this is an A/B question, this there any way I can wrote things like ForAll<OnStageXXX>(va);
to call OnStageXXX
on each element of a vector?
P.S. I tried with std::for_each
, but this makes the syntax even longer. I'm here to seek for a shorter syntax to do this, if any.
Upvotes: 2
Views: 341
Reputation: 48457
In c++17:
template <auto F, typename T>
void ForAll(std::vector<T>& vector) {
for (auto& item : vector) {
(item.*F)();
}
}
Usage:
ForAll<&A::OnStageOne>(va);
In c++20:
constexpr auto OnStageOne = [] (auto& t) -> decltype(t.OnStageOne()) {
return t.OnStageOne();
};
constexpr auto OnStageTwo = [] (auto& t) -> decltype(t.OnStageTwo()) {
return t.OnStageTwo();
};
constexpr auto OnStageThree = [] (auto& t) -> decltype(t.OnStageThree()) {
return t.OnStageThree();
};
template <auto F, typename T>
requires requires (T t) { F(t); }
void ForAll(std::vector<T>& vector) {
for (auto& item : vector) {
F(item);
}
}
Usage:
ForAll<OnStageOne>(va);
In c++11 / c++14 (and before without the ranged based for loop):
struct OnStageOne {
template <typename T>
void operator()(T& t) const { t.OnStageOne(); }
};
struct OnStageTwo {
template <typename T>
void operator()(T& t) const { t.OnStageTwo(); }
};
struct OnStageThree {
template <typename T>
void operator()(T& t) const { t.OnStageThree(); }
};
template <typename F, typename T>
void ForAll(std::vector<T>& vector) {
for (auto& item : vector) {
F{}(item);
}
}
Usage:
ForAll<OnStageOne>(va);
Upvotes: 2
Reputation: 73186
This answer approaches the question from an "XY problem" perspective:
Or, if this is an A/B question, this there any way I can wrote things like ForAll(va); to call OnStageXXX on each element of a vector?
It seems that your intent is to implement static polymorphism for the classes A
, B
and C
over a (not directly enforced) static interface, in this case, a number of OnStageXXX
member functions. Instead of solving the problem of "how to (static-)polymorphically invoke via pointers to member functions" you may want to look over how you implement the "static interface" of you classes.
As a first step, instead of using separate non-template member functions for the OnStageXXX
functions, you could implement them as a member function template parameterized over some tag, say an enum class
StageTag
. This would allow you to invoke the forAll(...)
function using the tag instead of a pointer to member function to choose which member function specialization to invoke. E.g.:
#include <iostream>
#include <vector>
enum class StageTag { kOne, kTwo, kThree };
class A {
public:
template <StageTag> void OnStage() const = delete;
};
template <> void A::OnStage<StageTag::kOne>() const {
std::cout << "A OnStage kOne\n";
}
template <> void A::OnStage<StageTag::kTwo>() const {
std::cout << "A OnStage kTwo\n";
}
template <> void A::OnStage<StageTag::kThree>() const {
std::cout << "A OnStage kThree\n";
}
class B {
public:
template <StageTag> void OnStage() const = delete;
};
template <> void B::OnStage<StageTag::kOne>() const {
std::cout << "B OnStage kOne\n";
}
template <> void B::OnStage<StageTag::kTwo>() const {
std::cout << "B OnStage kTwo\n";
}
template <> void B::OnStage<StageTag::kThree>() const {
std::cout << "B OnStage kThree\n";
}
template <StageTag kTag, typename T> void ForAll(const std::vector<T> &vector) {
for (auto &item : vector) {
item.template OnStage<kTag>();
}
}
int main() {
const std::vector<A> va(3);
const std::vector<B> vb(2);
ForAll<StageTag::kThree>(va);
// A OnStage kThree
// A OnStage kThree
// A OnStage kThree
ForAll<StageTag::kOne>(vb);
// B OnStage kOne
// B OnStage kOne
return 0;
}
Alternatively, instead of using specializations over StageTag
, you could use tag dispatch to dispatch to the appropriate non-template member function:
#include <iostream>
#include <vector>
enum class StageTag { kOne, kTwo, kThree };
class A {
private:
template <StageTag> struct ATag {};
public:
template <StageTag kTag> void OnStage() const { OnStage(ATag<kTag>{}); }
private:
void OnStage(ATag<StageTag::kOne>) const { std::cout << "A OnStage kOne\n"; }
void OnStage(ATag<StageTag::kTwo>) const { std::cout << "A OnStage kTwo\n"; }
void OnStage(ATag<StageTag::kThree>) const {
std::cout << "A OnStage kThree\n";
}
};
class B {
private:
template <StageTag> struct BTag {};
public:
template <StageTag kTag> void OnStage() const { OnStage(BTag<kTag>{}); }
private:
void OnStage(BTag<StageTag::kOne>) const { std::cout << "B OnStage kOne\n"; }
void OnStage(BTag<StageTag::kTwo>) const { std::cout << "B OnStage kTwo\n"; }
void OnStage(BTag<StageTag::kThree>) const {
std::cout << "B OnStage kThree\n";
}
};
template <StageTag kTag, typename T> void ForAll(const std::vector<T> &vector) {
for (auto &item : vector) {
item.template OnStage<kTag>();
}
}
int main() {
const std::vector<A> va(3);
const std::vector<B> vb(2);
ForAll<StageTag::kThree>(va);
// A OnStage kThree
// A OnStage kThree
// A OnStage kThree
ForAll<StageTag::kOne>(vb);
// B OnStage kOne
// B OnStage kOne
return 0;
}
You may also want to look into the Curiously Recurring Template Pattern to enforce your static interface.
Upvotes: 0