2015-03-13 3 views
4

Мне интересно, есть ли хороший общий способ записи обертки объекта, чтобы вы могли вызывать все методы, определенные классом, который он обертывает сам класс-оболочку (без явного переопределения интерфейса класса в классе оболочки). Например, два разных, произвольные классы:Обертка общего объекта

class InnerExample1 { 
public: 
    InnerExample1(int a, bool b) {...} 
    void DoSomething1(int a) {...} 
    bool DoSomething2(bool b) {...} 
    etc... 
}; 

class InnerExample2 { 
    InnerExample2(float a, int b) {...} 
    void DoWork(int a) {...} 
    bool GetResult(bool b) {...} 
    etc... 
}; 

Я хотел бы обобщенно определенный класс обертку таким образом, что я мог бы сделать:

Wrapper<InnerExample1> inner1; 
inner1.DoSomething(42); 
bool result = inner1.DoSomething2(false); 

и

Wrapper<InnerExample2> inner2; 
inner2.DoWork(21); 
bool result = inner2.GetResult(true); 

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

Я видел код для таких вещей, как фабричный шаблон, где с переменными аргументами общая фабрика может создавать экземпляр любого класса, а также обобщать привязку к одному методу-члену, но ничего, что было сделано для всего класс. Мне кажется, что это невозможно ... но люди на SO придумывают всевозможные вещи, которых у меня никогда не было бы :)

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

EDIT2: Чтобы быть конкретным, я пытаюсь написать общую оболочку для реализации шаблона ActiveObject. Я закончил создание базового класса «Active», который обрабатывает общие биты (управление внутренним потоком, вызовы вызова в очереди) и запись подклассов для каждого внутреннего типа (с их интерфейсом, имитируемым).

EDIT3: Ответ на вопрос @ id256 довольно интересен, я думаю, что в какой-то момент вернусь и попробую этот метод.

+0

Как насчет переопределения 'operator ->()'? – rodrigo

ответ

2

Вполне возможно, вы можете определить operator-> внутри шаблона Wrapper класса. На самом деле это может выглядеть следующим образом:

template <typename T> 
class Wrapper { 
    T* pT; 
public: 
// ... 
    T* operator->() { return pT; } 
// all the needed stuff to manage pT 
}; 

Этот подход используется, например, std::auto_ptr реализация STL в - посмотрите на http://www.cplusplus.com/reference/memory/auto_ptr/

EDIT: Если вам нужно добавить логику вызовов через объекты-оболочки , вышеприведенное решение неприемлемо.Однако вы можете попробовать некоторые функциональные возможности в сочетании с шаблонами (хотя этот подход является довольно сложным):

#include <functional> 
#include <iostream> 


class A { 
public: 
    int inc(int x) { 
     printf("A::inc %i\n", x); 
     return x+1; 
    }  
};  

template <typename _Function> 
struct function_logged : public _Function { 
    const char* log_msg; 

    function_logged(_Function fun, const char* _log_msg) 
    : _Function(fun), log_msg(_log_msg) { } 
}; 

template <typename T, typename U, typename V, T (U::*mf)(V)> 
function_logged<std::binder2nd<std::mem_fun1_t<T,U,V> > > create_function_logged(const V& arg, const char* log_msg) { 
    return function_logged<std::binder2nd<std::mem_fun1_t<T,U,V> > >(std::bind2nd(std::mem_fun(mf), arg), log_msg); 
} 

template <typename T> 
class Wrapper { 
    T* t; // pointer, not just a plain instance, because T may not have default constructor 
public: 
    Wrapper(T* _t) : t(_t) { }   

    template <typename T_mem_fun_tp> 
    typename T_mem_fun_tp::result_type operator()(const T_mem_fun_tp& t_mf) { 
     // Handle function object as you wish, for example print logging message 
     std::cout << t_mf.log_msg << '\n'; 
     return t_mf(t); 
    } 

    ~Wrapper() { delete t; } 
}; 

int main() { 
    Wrapper<A> wA(new A()); 
    int y = wA(create_function_logged<int,A,int,&A::inc>(123, "calling A::inc from Wrapper")); 
    std::cout << "wA.f(123)=" << y << '\n'; 
    return 0; 
} 

Выход:

calling A::inc from Wrapper 
A::inc 123 
wA.f(123)=124 

EDIT2: Кроме того, есть возможность переопределить operator->* внутри Wrapper и вернуть какое-то прокси-объекта, который содержит указатель на экземпляр и указатель на этот метод. При таком подходе вы можете контролировать момент выполнения метода, а также до или после обработки прокси-объектов. Описание этого метода можно найти в http://aristeia.com/Papers/DDJ_Oct_1999.pdf

+0

Действительно, определенно решение, если 'wrapper', действующее как указатель (семантически), является приемлемым. – aruisdante

+0

Мне нужно будет отредактировать мой пост - я пытался оставить детали, чтобы все было просто, но, полагаю, я забыл слишком много. Мне нужен класс-оболочка, чтобы иметь возможность вставить дополнительную логику для каждого вызова. – bbaldino

+0

Это действительно интересно! Единственное, что я вижу здесь, это то, что код в 'function_logged', кажется, предполагает, что функция примет один аргумент? Если только std :: mem_fun поддерживает пакет arg, который может это сделать. Кроме того, я надеялся, что обертка сделает сами привязки, поэтому это потребует, чтобы обертка знала типы возвращаемого типа/аргумента для каждой из функций. – bbaldino

0

В соответствии с вашим комментарием (и редактированием) наиболее близким к тому, что вы можете получить, является выполнение некоторой логики до вызова метода (или доступ к атрибуту), используя тот факт, что оператор-> должен использоваться с метод или имя атрибута с правой стороны. К сожалению, у вас нет способа сказать, какой именно.

#include <iostream> 

template<typename T> 
class wrapper { 
    T obj; 
    public: 
    T* operator->() { 
    std::cout << "Method called(or attribute accessed). Can't tell which one." << std::endl; 
    return &obj; 
    } 
}; 

struct wrapped { 
    void do_nothing() {} 
}; 

int main() { 
    wrapper<wrapped> a; 
    a->do_nothing(); 
} 
+0

Мне действительно нужно вставить некоторую логику в сам класс-оболочку, который будет выполнять некоторую работу для каждого метода, который называется. – bbaldino

+0

Если вам нужно изменить поведение метода, нет никакого способа обойти его или использовать делегирование. Вы имеете в виду выполнение ** той же работы ** для любого метода, называемого (например, запись в файл журнала, например)? –

+0

Это известно как http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern –

1

Если вы не могут наследовать от T вы можете переписать оператор ->, чтобы дать доступ к обернутому классу

class Wrapped { 
public: 
    void DoSomething(); 
}; 

template<class T> 
class Wrapper { 
public: 
    T * operator->() { 
     return &t; 
    } 

private: 
    T t; 
}; 

int main() { 
    Wrapper<Wrapped> w; 
    w->DoSomething(); 
} 
+0

Как указано OP в ответ на ответ ниже, в котором предлагается тот же вопрос, проблема в том, что они действительно хотят, чтобы «Wrapper» правильно проксировал методы обернутых классов, чтобы они могли добавлять дополнительные до/после операций в вызовы методов. – aruisdante

1

Это не особенно инкапсулированному но вы можете использовать шаблон функции, которая принимает указатель на функцию члена и VARIADIC аргумент пакет:

template<typename Inner> 
class Wrapper { 
private: 
    Inner inner; 
public: 

    template<typename ...Args> 
    Wrapper(Args&&... args) : inner(std::forward<Args>(args)...) { } 

    template<typename ...Args, typename R> 
    R forward(R (Inner::* function)(Args...), Args &&...args) { 
    return (inner.*function)(std::forward<Args>(args)...); 
    } 
}; 


Wrapper<InnerExample1> wrapper1(1, true); 
wrapper1.forward(&InnerExample1::DoSomething1, 42); 
bool result = wrapper1.forward(&InnerExample1::DoSomething2, false); 

Live demo