fmsf
fmsf

Reputation: 37167

In c++ making a function that always runs when any other function of a class is called

C++ has so much stuff that I don't know.

Is there any way to create a function within a class, that will always be called whenever any other function of that class is called? (like making the function attach itself to the first execution path of a function)

I know this is tricky but I'm curious.

Upvotes: 3

Views: 1879

Answers (9)

Scott Langham
Scott Langham

Reputation: 60381

Yes-ish, with a bit of extra code, some indirection and another class and using the -> instead of the . operator.

// The class for which calling any method should call PreMethod first.
class DogImplementation
{
public:
   void PreMethod();
   void Bark();
private:
   DogImplementation(); // constructor private so can only be created via smart-pointer.
   friend class Dog; // can access constructor.
};

// A 'smart-pointer' that wraps a DogImplementation to give you
// more control.
class Dog
{
public:
   DogImplementation* operator -> ()
   {
       _impl.PreMethod();
       return &_impl;
   }
private:
   DogImplementation _impl;
};

// Example usage of the smart pointer. Use -> instead of .
void UseDog()
{
  Dog dog;
  dog->Bark();    // will call DogImplementation::PreMethod, then DogImplementation::Bark
}

Well.. something roughly along those lines could be developed into a solution that I think would allow you to do what you want. What I've sketched out there probably won't compile, but is just to give you a starting point.

Upvotes: 15

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507105

You can derive from this class template:

namespace detail {
struct const_tag;
struct nonconst_tag;

/* T is incomplete yet when pre_call is instantiated. 
 * so delay lookup of ::impl until call to operator-> 
 * happened and this delay_lookup is instantiated */
template<typename U, typename>
struct delay_lookup; 

template<typename U>
struct delay_lookup<U, nonconst_tag>
{
    typedef typename U::template get_impl<
        typename U::derived_type>::type impl_type;
    impl_type* u;
    delay_lookup(impl_type* u):u(u) { }
    impl_type* operator->() { return u; }
};

template<typename U>
struct delay_lookup<U, const_tag> {
    typedef typename U::template get_impl<
        typename U::derived_type>::type const impl_type;
    impl_type* u;
    delay_lookup(impl_type* u):u(u) { }
    impl_type* operator->() { return u; }
};

} // detail::

template<typename T>
struct pre_call {
private:
    friend class detail::delay_lookup<pre_call, detail::const_tag>;
    friend class detail::delay_lookup<pre_call, detail::nonconst_tag>;
    typedef T derived_type;

    /* pre_call is the friend of T, and only it
     * is allowed to access T::impl */
    template<typename U> struct get_impl {
        typedef typename U::impl type;
    };

protected:
    typedef boost::function<void(T const&)> fun_type;
    fun_type pre;

    template<typename Fun>
    pre_call(Fun pre):pre(pre) { }

public:
    /* two operator->: one for const and one for nonconst objects */
    detail::delay_lookup<pre_call, detail::nonconst_tag> operator->() { 
        pre(*get_derived()); 
        return detail::delay_lookup<pre_call, 
            detail::nonconst_tag>(&get_derived()->d);
    }

    detail::delay_lookup<pre_call, detail::const_tag> operator->() const { 
        pre(*get_derived()); 
        return detail::delay_lookup<pre_call, 
            detail::const_tag>(&get_derived()->d);
    }

private:
    T * get_derived() { 
        return static_cast<T *>(this); 
    }

    T const* get_derived() const { 
        return static_cast<T const*>(this); 
    }
};

And use it like this:

struct foo : pre_call<foo> {
private:
    /* stuff can be defined inline within the class */
    struct impl { 
        void some() const {
            std::cout << "some!" << std::endl;
        }

        void stuff()  {
            std::cout << "stuff!" << std::endl;
        }
    };

    void pre() const { 
        std::cout << "pre!" << std::endl;
    }

    friend struct pre_call<foo>;
    impl d;

public:
    foo():pre_call<foo>(&foo::pre) { }    
};

int main() {
    foo f;
    f->some();
    f->stuff();
    // f.some(); // forbidden now!
}

Previously i had a version that called post functions too. But i dropped it. It would have needed additional work. However, i would still not recommend you to do this "call function automatically" thingy. Because one can easily forget to use the operator-> syntax and just use the dot - and suddenly have the pre function not called

Update: The version above takes care of that, so one cannot accidentally call functions with the dot anymore.

Upvotes: 4

Uli Gerhardt
Uli Gerhardt

Reputation: 14001

If I'm not mistaken this is a feature of what is called Aspect Oriented Programming.

Upvotes: 2

Choover
Choover

Reputation:

I'm not sure exactly what your restrictions are, so I don't know if this helps. If your object a singleton, you could stick all the code that gets called for every function call in the call to get the singleton.

Downside is all your other functions calls get ugly. And you may not be able to make the object a singleton.

Upvotes: 0

ChrisW
ChrisW

Reputation: 56123

Yes. :-)

  • Wrap the object in a smart pointer
  • Invoke the object's special function automatically from the smart pointer's dereferencing operators (so that the special function is invoked whenever a client dereferences the smart pointer).

Upvotes: 9

jdmichal
jdmichal

Reputation: 11162

As others have said, there is no "automatic" way to do this. As in, the C++ standard does not define a way to do this.

However, if you are going to go the route of putting a method call at the beginning of every method, I would recommend you instead store and invoke a method pointer instead. This will allow you to dynamically modify which method is being called, including none with some careful programming and setting the method to null.

Upvotes: 0

kshahar
kshahar

Reputation: 10513

The short answer: No.

The long answer: there is no such thing in the C++ standard.

Upvotes: 2

user19302
user19302

Reputation:

Without some insane code injection, this is not possible. However, you can of course call that function manually.

Upvotes: 2

crashmstr
crashmstr

Reputation: 28573

There is no "automatic" way to do this. You would need to add a call to the function in each class method.

Upvotes: 3

Related Questions