В C ++ создание функции, которая всегда выполняется, когда вызывается любая другая функция класса - PullRequest
3 голосов
/ 06 февраля 2009

C ++ имеет столько всего, чего я не знаю.

Есть ли способ создать функцию внутри класса, которая будет вызываться всегда, когда вызывается любая другая функция этого класса? (например, заставить функцию присоединиться к первому пути выполнения функции)

Я знаю, что это сложно, но мне любопытно.

Ответы [ 9 ]

14 голосов
/ 06 февраля 2009

Да, с небольшим количеством дополнительного кода, некоторой косвенностью и другим классом и использованием -> вместо. оператор.

// 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
}

Ну ... что-то в этом роде может быть превращено в решение, которое, я думаю, позволит вам делать то, что вы хотите. То, что я набросал там, вероятно, не скомпилируется, а просто даст вам отправную точку.

9 голосов
/ 06 февраля 2009

Да. : -)

  • Обернуть объект в умный указатель
  • Автоматически вызывать специальную функцию объекта из операторов разыменования интеллектуального указателя (чтобы специальная функция вызывалась всякий раз, когда клиент разыменовывает интеллектуальный указатель).
4 голосов
/ 06 февраля 2009

Вы можете получить из этого шаблона класса:

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); 
    }
};

И используйте это так:

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!
}

Ранее у меня была версия, которая тоже вызывала функции почты. Но я бросил это. Это потребовало бы дополнительной работы. Тем не менее, я все равно не рекомендую вам сделать эту "функцию вызова автоматически". Потому что можно легко забыть использовать синтаксис оператора-> и просто использовать точку - и внезапно пре-функция не вызывается

Обновление : вышеупомянутая версия позаботится об этом, поэтому нельзя больше случайно вызывать функции с точкой.

3 голосов
/ 06 февраля 2009

Нет "автоматического" способа сделать это. Вам необходимо добавить вызов функции в каждый метод класса.

2 голосов
/ 06 февраля 2009

Если я не ошибаюсь, это особенность того, что называется Аспектно-ориентированное программирование .

2 голосов
/ 06 февраля 2009

Краткий ответ: Нет.

Длинный ответ: в стандарте C ++ такого нет.

2 голосов
/ 06 февраля 2009

Без некоторой безумной инъекции кода это невозможно. Однако вы, конечно, можете вызвать эту функцию вручную.

0 голосов
/ 06 февраля 2009

Я точно не знаю, каковы ваши ограничения, поэтому я не знаю, поможет ли это. Если ваш объект одноэлементный, вы можете прикрепить весь код, который вызывается для каждого вызова функции в вызове, чтобы получить одноэлементный код.

Недостатком является то, что все остальные вызовы функций становятся ужасными. И вы, возможно, не сможете сделать объект одиночным.

0 голосов
/ 06 февраля 2009

Как уже говорили другие, не существует "автоматического" способа сделать это. Например, стандарт C ++ не определяет способ сделать это.

Тем не менее, если вы собираетесь пойти по пути вызова метода в начале каждого метода, я бы порекомендовал вам вместо этого сохранить и вызвать указатель метода. Это позволит вам динамически изменять, какой метод вызывается, в том числе ни один с некоторым тщательным программированием и установкой метода на нуль.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...