0

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    I don't think you can prevent doing a cast. IMHO, your goal should be to protect normal users from inadvertenly misusing your code, not trying to prevent malicious users from purposefully doing so. If someone is willing to jump through enough hoops, they can always use pointer arithmetic or hack around templates to still access protected things. – francoisr Aug 03 '20 at 20:05
  • 1
    `public` / `private` specifiers are helpers / training-wheels / ways to prevent you from making common mistakes. They are *not* security measures and they are *not* absolute means of preventing access. For example; the C-style cast ignores access specifiers. For another example, see [Access to private members. That's easy!](https://bloglitb.blogspot.com/2010/07/access-to-private-members-thats-easy.html) as well as [Uses and Abuses of Access Rights](http://www.gotw.ca/gotw/076.htm). – Jesper Juhl Aug 03 '20 at 20:11
  • Why is it so important for you to prohibit a static_cast, when someone can just reinterpret_cast if they really want to? – einpoklum Aug 03 '20 at 20:11
  • _Why_ do you really want to avoid it? – Asteroids With Wings Aug 03 '20 at 20:12
  • well, let's imagine you want to program your object bullet to say "i crashed" when it crashes. So just do `bullet.collision += [](...){std::cout << "hey i crashed\n"; }`. So it only happens when it crashes. (activated by a collision manager or another special and specific class). But if `bulltet.collision()` can be called anywhere, there's no reason to do a program the class with anything. (i want to avoid the cast to prevent anyone to call `operator()`). Anyway, i think there may be no solution to what i'm asking so i just follow francoisr suggestion. – Sebastian R. Aug 03 '20 at 20:28
  • Well at the moment the consumer doesn't even need a cast to bypass your protection, he can just access `bullet.collision.d_callbacks` – Ben Voigt Aug 03 '20 at 21:35
  • Yeah, I mean. You are right but those members are "unprotected" because this is not the finalized and polished class. Since they were not part of the problem I was asking, i did not see the need to "protect" them in this example. Anyway, thanks for the advice. should i close or delete this question?. I'm not very familiar with StackOverflow's way of "working". – Sebastian R. Aug 03 '20 at 23:58

0 Answers0