Context:
I have been trying to use Dietmar Kühl's delegate class from this answer in a way that only the owner class could activate it, and I almost achieved it.
The code is the following:
// Example program
#include <algorithm>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
class Entity;
struct EvArgs {
Entity* Origin;
EvArgs(){
}
};
template <typename Signature>
struct delegate;
template <typename Args>
struct delegate<void(Args*)>
{
struct base {
virtual ~base() {}
virtual void do_call(Args* args) = 0;
};
template <typename T>
struct call : base {
T d_callback;
template <typename S>
call(S&& callback) : d_callback(std::forward<S>(callback)) {}
void do_call(Args* args) {
this->d_callback(std::forward<Args*>(args));
return;
}
};
std::vector<std::unique_ptr<base>> d_callbacks;
std::vector<std::unique_ptr<base>> d_tmp_callbacks;
delegate(delegate const&) = delete;
void operator=(delegate const&) = delete;
public:
delegate() {
if (!std::is_base_of<EvArgs, Args>::value)
throw "specified type is not derived class from EvArgs\n";
}
~delegate() {
d_callbacks.clear();
d_tmp_callbacks.clear();
}
template <typename T>
delegate& operator+= (T&& callback) {
this->d_callbacks.emplace_back(new call<T>(std::forward<T>(callback)));
return *this;
}
template <typename T>
delegate& operator<< (T&& callback) {
this->d_tmp_callbacks.emplace_back(new call<T>(std::forward<T>(callback)));
return *this;
}
};
template<typename Signature>
struct action_delegate;
template<typename Args>
struct action_delegate<void(Args*)> : public delegate<void(Args*)>{
delegate<void(Args*)>& getBase(){
return *static_cast<delegate<void(Args*)>*>(this);
}
void operator()(Args* args) {
for (auto& callback : this->d_callbacks) callback->do_call(args);
for (auto& callback : this->d_tmp_callbacks) callback->do_call(args);
this->d_tmp_callbacks.clear();
delete args;
}
};
class instance{
private:
action_delegate<void(EvArgs*)> _collision;
public:
delegate<void(EvArgs*)>& collision = _collision.getBase();
};
int main(){
instance i;
i.collision << [](EvArgs* a){
std::cout << "random calling\n";
};
//i want to prohibit this:
(static_cast< action_delegate<void(EvArgs*)>& >(i.collision))(new EvArgs());
}
Problem:
As the action_delegate is a private member, only the instance class can call its activation with operator(). And as delegate is public, operator <<() and operator +=() are accessible outside the class.
The problem is that there is a way to cast delegate into action_delegate, thus it's possible to activate the delegate with operator() outside the class. I really want to avoid that.
The original creator of this concept is accessible through the link at the start of the commentary body.
Related
I want to create an event system that uses lambda functions as its subscribers/listeners, and an event type to assign them to the specific event that they should subscribe to. The lambdas should have variable arguments, as different kinds of events use different kinds of arguments/provide the subscribers with different kinds of data.
For my dispatcher, I have the following:
class EventDispatcher {
public:
static void subscribe(EventType event_type, std::function<void(...)> callback);
void queue_event(Event event);
void dispatch_queue();
private:
std::queue<Event*> event_queue;
std::map<EventType, std::function<void(...)>> event_subscribers;
};
No issues here, but when I go to implement the subscribe() function in my .cpp file, like this:
void EventDispatcher::subscribe(EventType event_type, std::function<void(...)> callback) {
... (nothing here yet)
}
The IDE shows me this:
Implicit instantiation of undefined template 'std::function<void (...)>'
Don't try to plop event callbacks with different parameter types directly into a single map.
Instead, create a template to store the callback (templated by the parameter types), and store pointers to its non-template base.
Here's how I would do it:
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <queue>
#include <tuple>
#include <typeindex>
#include <typeinfo>
#include <type_traits>
#include <utility>
struct Event
{
virtual ~Event() = default;
};
struct Observer
{
virtual ~Observer() = default;
virtual void Observe(const Event &e) const = 0;
};
template <typename ...P>
struct BasicEvent : Event
{
std::tuple<P...> params;
BasicEvent(P ...params) : params(std::move(params)...) {}
struct EventObserver : Observer
{
std::function<void(P...)> func;
template <typename T>
EventObserver(T &&func) : func(std::forward<T>(func)) {}
void Observe(const Event &e) const override
{
std::apply(func, dynamic_cast<const BasicEvent &>(e).params);
}
};
// We need a protected destructor, but adding one silently removes the move operations.
// And adding the move operations removes the copy operations, so we add those too.
BasicEvent(const BasicEvent &) = default;
BasicEvent(BasicEvent &&) = default;
BasicEvent &operator=(const BasicEvent &) = default;
BasicEvent &operator=(BasicEvent &&) = default;
protected:
~BasicEvent() {}
};
class EventDispatcher
{
public:
template <typename E>
void Subscribe(typename E::EventObserver observer)
{
event_subscribers.insert_or_assign(typeid(E), std::make_unique<typename E::EventObserver>(std::move(observer)));
}
template <typename E>
void QueueEvent(E &&event)
{
event_queue.push(std::make_unique<std::remove_cvref_t<E>>(std::forward<E>(event)));
}
void DispatchQueue()
{
while (!event_queue.empty())
{
Event &event = *event_queue.front();
event_subscribers.at(typeid(event))->Observe(event);
event_queue.pop();
}
}
private:
std::queue<std::unique_ptr<Event>> event_queue;
std::map<std::type_index, std::unique_ptr<Observer>> event_subscribers;
};
struct EventA : BasicEvent<> {using BasicEvent::BasicEvent;};
struct EventB : BasicEvent<> {using BasicEvent::BasicEvent;};
struct EventC : BasicEvent<int, int> {using BasicEvent::BasicEvent;};
int main()
{
EventDispatcher dis;
dis.Subscribe<EventA>([]{std::cout << "Observing A!\n";});
dis.Subscribe<EventB>([]{std::cout << "Observing B!\n";});
dis.Subscribe<EventC>([](int x, int y){std::cout << "Observing C: " << x << ", " << y << "!\n";});
dis.QueueEvent(EventA());
dis.QueueEvent(EventB());
dis.QueueEvent(EventC(1, 2));
dis.DispatchQueue();
}
You could create your own version of std::function that accepts functions of any signature using type erasure. This will require some heavy lifting though. I will provide a solution for void functions which requires C++17, because we will use std::any.
I will walk you through the steps first and then provide a full solution in code.
First you create some function_traits that capture the number and type of arguments of any kind of function using template meta-programming. We can "borrow" from here.
Then you create a class VariadicVoidFunction that has a templated call operator.
This call operator creates a std::vector<std::any> and passes it to the invoke method of a member of VariadicVoidFunction, which is a (resource-owning smart) pointer of type VariadicVoidFunction::Concept.
VariadicVoidFunction::Concept is an abstract base class with a virtual invoke method that accepts std::vector<std::any>.
VariadicVoidFunction::Function is a class template, where the template parameter is a function. It stores this function as member and inherits VariadicVoidFunction::Concept. It implements the invoke method. Here, we can std::any_cast the vector elements back to the expected types, which we can extract using function_traits. This allows us to call the actual function with the correct argument types.
VariadicVoidFunction gets a templated constructor accepting any kind of function F. It creates an instance of type VariadicVoidFunction::Function<F> and stores it in an owning (smart) pointer.
#include <memory>
#include <vector>
#include <any>
// function_traits and specializations are needed to get arity of any function type
template<class F>
struct function_traits;
// ... function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};
// ... normal function
template<class R, class... Args>
struct function_traits<R(Args...)>
{
static constexpr std::size_t arity = sizeof...(Args);
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
};
};
// ... non-const member function
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};
// ... const member function
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C const&,Args...)>
{};
// ... functor (no overloads allowed)
template<class F>
struct function_traits
{
private:
using call_type = function_traits<decltype(&F::operator())>;
public:
static constexpr std::size_t arity = call_type::arity - 1;
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename call_type::template argument<N+1>::type;
};
};
template<class F>
struct function_traits<F&> : public function_traits<F>
{};
template<class F>
struct function_traits<F&&> : public function_traits<F>
{};
// type erased void function taking any number of arguments
class VariadicVoidFunction
{
public:
template <typename F>
VariadicVoidFunction(F const& f)
: type_erased_function{std::make_shared<Function<F>>(f)} {}
template <typename... Args>
void operator()(Args&&... args){
return type_erased_function->invoke(std::vector<std::any>({args...}));
}
private:
struct Concept {
virtual ~Concept(){}
virtual void invoke(std::vector<std::any> const& args) = 0;
};
template <typename F>
class Function : public Concept
{
public:
Function(F const& f) : func{f} {}
void invoke(std::vector<std::any> const& args) override final
{
return invoke_impl(
args,
std::make_index_sequence<function_traits<F>::arity>()
);
}
private:
template <size_t... I>
void invoke_impl(std::vector<std::any> const& args, std::index_sequence<I...>)
{
return func(std::any_cast<typename function_traits<F>::template argument<I>::type>(args[I])...);
}
F func;
};
std::shared_ptr<Concept> type_erased_function;
};
You can use it like this:
#include <unordered_map>
#include <iostream>
int main()
{
VariadicVoidFunction([](){});
std::unordered_map<size_t, VariadicVoidFunction> map =
{
{0, VariadicVoidFunction{[](){ std::cout << "no argument\n"; }} },
{1, VariadicVoidFunction{[](int i){ std::cout << "one argument\n"; }} },
{2, VariadicVoidFunction{[](double j, const char* x){ std::cout<< "two arguments\n"; }} }
};
map.at(0)();
map.at(1)(42);
map.at(2)(1.23, "Hello World");
return 0;
}
no argument
one argument
two arguments
Demo on Godbolt Compiler explorer
Note that this is a prototypical solution to get you started. One downside is that all arguments will be copied into the std::any You could avoid this by passing pointers to std::any, but you have to be careful with lifetimes when you do this.
After the input from #joergbrech and #HolyBlackCat I made this
enum class EventType {
WindowClosed, WindowResized, WindowFocused, WindowLostFocus, WindowMoved,
AppTick, AppUpdate, AppRender,
KeyPressed, KeyRelease,
MouseButtonPressed, MouseButtonRelease, MouseMoved, MouseScrolled,
ControllerAxisChange, ControllerButtonPressed, ControllerConnected, ControllerDisconnected
};
class IEvent {
public:
IEvent(EventType event_type) {
this->event_type = event_type;
}
EventType get_event_type() {
return event_type;
}
private:
EventType event_type;
};
class IEventSubscriber {
public:
/**
* #param event The event that is passed to the subscriber by the publisher; should be cast to specific event
* */
virtual void on_event(IEvent *event) = 0;
EventType get_event_type() {
return event_type;
}
protected:
explicit IEventSubscriber(EventType event_type) {
this->event_type = event_type;
}
private:
EventType event_type;
};
class FORGE_API EventPublisher {
public:
static void subscribe(IEventSubscriber *subscriber);
static void queue_event(IEvent *event);
static void dispatch_queue();
private:
static std::queue<IEvent*> event_queue;
static std::set<IEventSubscriber*> event_subscribers;
};
I've tested it and I get the expected result from this solution. For the full code solution -> https://github.com/F4LS3/forge-engine
std::function has no specialization for variadic function types.
You likely want std::function<void()>.
class Base {};
class Derived : public Base {};
class SomeClass
{
template<typename T>
static void SetContent(T* pChild, OVariant content)
{
LOG_ASSERT(0, "All classes must be specialized!. Please provide implementation for this class.");
}
};
template <>
void SomeClass::SetContent(Base* value)
{
LOG_TRACE("Yay!");
}
int main() {
SomeClass foo;
Derived derived;
foo.SetContent(&derived);//want it to call SomeClass::SetContent(Base* value)
return 0;
}
When I call foo.SetContent(derived), I get the Assert. Is it not possible for the derived class to use the specialization for it's base class?
You can convert a Derived* to a Base*, but I think you rather want to specialize for all T that have Base as base
#include <type_traits>
#include <iostream>
class Base {};
class Derived : public Base {};
template <typename T,typename = void>
struct impl {
void operator()(T*) {
std::cout <<"All classes must be specialized!. Please provide implementation for this class.\n";
}
};
template <typename T>
struct impl<T,std::enable_if_t<std::is_base_of_v<Base,T>>> {
void operator()(T*) {
std::cout << "Yay\n";
}
};
class SomeClass
{
public:
template<typename T>
static void SetContent(T* pChild)
{
impl<T>{}(pChild);
}
};
struct bar{};
int main() {
SomeClass foo;
Derived derived;
foo.SetContent(&derived);
bar b;
foo.SetContent(&b);
}
Output:
Yay
All classes must be specialized!. Please provide implementation for this class.
//want it to call SomeClass::SetContent(Base* value)
Note that if the template argument is deduced, it will be deduced as Derived not as Base and the argument is Derived*. SomeClass::SetContent<Base>(&derived); would already work as expected with your code (because Derived* can be converted to Base*).
A workaround would be to have all SetContent's explicit specializations to form an overload set. You will have to do it yourself:
#include <iostream>
#include <utility>
#include <functional>
class Base {};
class Derived : public Base {};
template <class... T>
struct Overloads : T... {
Overloads(const T &... t) : T(t)... {}
using T::operator()...;
};
template <class R, class... Args>
struct FunctionP {
using F = R(Args...);
FunctionP(F *t) : t_(t) {}
R operator()(Args... args) const {
return std::invoke(t_, std::forward<Args>(args)...);
}
F *t_;
};
struct SomeClass {
template<typename T>
static void SetContent(T *x) {
Overloads o(FunctionP(&SetContentImpl<Base>)); // enumerates all the specializations here
if constexpr (std::is_invocable_v<decltype(o), T *>) {
o(x);
} else {
SetContentImpl(x);
}
}
template<typename T>
static void SetContentImpl(T *) {
std::cout << "1";
}
};
template <>
void SomeClass::SetContentImpl(Base *) {
std::cout << "2";
}
int main() {
SomeClass foo;
Derived derived;
foo.SetContent(&derived);//want it to call SomeClass::SetContent(Base* value)
return 0;
}
godbolt
Is there a way to automatically create one method per variadic template argument?
For example, in the code below, I want to be forced to override void x(a &v) and void x(b &v) in class i:
#include <type_traits>
#include <stdlib.h>
#include <stdio.h>
class a {
};
class b {
};
template <typename ...T>
class t {
public:
virtual void x(T &v) = 0;
};
class i : public t<a, b>
{
};
int
main (int argc, char *argv[])
{
i ii;
return 0;
}
You can make a t_impl that holds the virtual function for a single T like
template <typename T>
class t_impl
{
public:
virtual void x(T &v) = 0;
};
and then t would inherit from it like
template <typename ...T>
class t : t_impl<T>... // can use public, protected or private inheritance
{
public:
using t_impl<T>::x...; // used to import t_impl<T>::x into the public space
// any additional common members
};
Instead of making t a variadic template, just template it on a single type:
template <typename T>
class t {
public:
virtual void x(T &v) = 0;
};
and inherit base classes like this instead:
class i : public t<a>, t<b>
{
virtual void x(a &v) {}
virtual void x(b &v) {}
};
Here's a demo.
Let me just give a code example.
#include <iostream>
#include <utility>
#include <tuple>
template <typename Service>
struct SubscriberImpl {
virtual void handleService(Service const&) = 0;
};
template <typename...ServiceType>
struct Subscriber : SubscriberImpl<ServiceType>... {
};
struct IntService {};
struct FloatService {};
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
// wont work
void handleService(IntService const& service) override {
m_statusUpdater.updateService(service);
}
void handleService(FloatService const& service) override {
m_statusUpdater.updateService(service);
}
StatusUpdatePolicy m_statusUpdater;
};
struct DummyPolicy {
void updateService(IntService const& service) {
m_i = 42;
std::cout << m_i << "\n";
}
void updateService(FloatService const& service) {
m_f = 3.14f;
std::cout << m_f << "\n";
}
int m_i;
float m_f;
};
int main() {
StatusUpdater<DummyPolicy, IntService, FloatService> su(DummyPolicy{});
su.handleService(IntService{});
su.handleService(FloatService{});
}
Here Subscriber has a pure virtual function handleService(ServiceType const) for each template parameter in pack ServiceType.... So I have to override each one on StatusUpdater. Here, I have provided the ones I need by hand for IntService and FloatService, knowing I will be only needing those in this minimal example. But I want to be able to provide an override for whatever there is in the pack ServiceType.... All of them will call updateService method of the given policy anyways.
Please note that Subscriber comes from an external library and I cannot modify its definition.
You cannot put such implementations directly into the class, you have to inherit them (similarly to how Subscriber inherits from multiple SubscriberImpl instantiations). However, to override them all and still keep your class polymorphically usable as a Subscriber, you will have to inherit them "sequentially" instead of "in parallel." Additionally, the Curiously recurring template pattern can be used to give all the implementations access to the final overriding object:
template <class Self, class SubscriberClass, class... ServiceTypes>
struct StatusUpdaterOverride;
template <class Self, class SubscriberClass, class ThisType, class... RemainingTypes>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType, RemainingTypes...> : StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
using StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>::handleService;
};
template <class Self, class SubscriberClass, class ThisType>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType> : SubscriberClass
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
};
template <class StatusUpdatePolicy, class... ServiceType>
struct StatusUpdater : StatusUpdaterOverride<StatusUpdater<StatusUpdatePolicy, ServiceType...>, Subscriber<ServiceType...>, ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
StatusUpdatePolicy m_statusUpdater;
};
[Live example]
I can't see a solution to do exactly what you want. However you can achieve the same behavior without needing the virtuality at all. I initially thought about a CRTP solution just like #Angew's answer and then came up with another possibility:
You could edit your Subscriber class like this:
template <typename ServiceType>
class Subscriber {
public:
template <typename Handler>
void handleService(ServiceType const& service, Handler&& hdler) {
// Maybe give `updateService` a broader name that can extend to other service handlers
std::forward<Handler>(hdler).updateService(service);
}
};
With that, your client code becomes:
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType>...
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
template <typename ServiceT>
void handleService(ServiceT const& service) override {
Subscriber<ServiceT>::handleService(service, m_statusUpdater);
}
StatusUpdatePolicy m_statusUpdater;
};
template <typename T, typename C>
class CSVWriter{
template <typename PrinterT>
void write(std::ostream& stream, const PrinterT& printer){
}
};
I want to check whether there exists at least two overloads PrinterT::operator()(T*) and PrinterT::operator()(C*)
PrinterT may or may not inherit from std::unary_function
What concept Checking Classes I need to use here ?
(I am not using C++11)
You can use something like that
#include <iostream>
#include <boost/concept/requires.hpp>
#include <boost/concept/usage.hpp>
template <class Type, class Param>
class has_operator_round_brackets_with_parameter
{
public:
BOOST_CONCEPT_USAGE(has_operator_round_brackets_with_parameter)
{
_t(_p);
}
private:
Type _t;
Param _p;
};
struct X {};
struct Y {};
struct Test1
{
void operator() (X*) const { }
};
struct Test2: public Test1
{
void operator() (X*) const { }
void operator() (Y*) const { }
};
template <class T, class C>
struct CSVWriter
{
template <class PrinterT>
BOOST_CONCEPT_REQUIRES(
((has_operator_round_brackets_with_parameter<PrinterT, T*>))
((has_operator_round_brackets_with_parameter<PrinterT, C*>)),
(void)) write(std::ostream& stream, const PrinterT& printer)
{
}
};
int main()
{
CSVWriter<X, Y> w;
// w.write<Test1>(std::cout, Test1()); // FAIL
w.write<Test2>(std::cout, Test2()); // OK
return 0;
}