我正在尝试创建一个类,它为泛型类添加功能,而不直接与包装类接口。一个很好的例子是智能指针。具体来说,我想创建一个包装器来缓存通过包装器调用的一个(或任何?)方法的所有i / o。理想情况下,缓存包装器具有以下属性:
例如,像这样使用它真的很好:
CacheWrapper<NumberCruncher> crunchy;
...
// do some long and ugly calculation, caching method input/output
result = crunchy->calculate(input);
...
// no calculation, use cached result
result = crunchy->calculate(input);
虽然像这样的傻瓜会没问题:
result = crunchy.dispatch (&NumberCruncher::calculate, input);
我觉得这应该可以在C ++中实现,尽管可能在某个地方有一些句法体操。
有什么想法吗?
答案 0 :(得分:1)
我不认为只使用包装器就可以轻松完成,因为您必须拦截IO调用,因此包装类会将代码置于错误的层。实质上,您希望替换对象下面的IO代码,但是您尝试从顶层执行此操作。如果您将代码视为洋葱,那么您正在尝试修改外部皮肤以影响两三层的内容;恕我直言,建议设计可能需要重新考虑。
如果你试图以这种方式包装/修改的类允许你传入流(或者你使用的任何IO机制),那么用那个代替一个缓存就可以了;从本质上讲,这也是你试图用你的包装器实现的目标。
答案 1 :(得分:1)
看起来这是一个简单的任务,假设“NumberCruncher”有一个已知的接口,让我们说int运算符(int)。 请注意,您需要使其更复杂以支持其他接口。为了做到这一点,我正在添加另一个模板参数,一个适配器。适配器应将某些接口转换为已知接口。这是使用静态方法的简单和愚蠢的实现,这是一种方法。另外看看Functor是什么。
struct Adaptor1 {
static int invoke(Cached1 & c, int input) {
return(c.foo1(input));
}
};
struct Adaptor2 {
static int invoke(Cached2 & c, int input) {
return(c.foo2(input));
}
};
template class CacheWrapper<typename T, typeneame Adaptor>
{
private:
T m_cachedObj;
std::map<int, int> m_cache;
public:
// add c'tor here
int calculate(int input) {
std::map<int, int>::const_iterator it = m_cache.find(input);
if (it != m_cache.end()) {
return(it->second);
}
int res = Adaptor::invoke(m_cachedObj, input);
m_cache[input] = res;
return(res);
}
};
答案 2 :(得分:1)
我想我有你想要的答案,或者至少我差不多了。它使用您建议的发送方式很傻,但我认为它符合您提出的前两个标准,并且或多或少符合第三个标准。
基本思想是创建一个模板类,其参数是要包装的对象的类,使用模板dispatch
方法,其参数是成员函数的参数和返回类型。 dispatch方法查找传入的成员函数指针以查看它是否之前已被调用。如果是这样,它将检索先前方法参数和计算结果的记录,以返回给予dispatch的参数的先前计算值,或者如果它是新的则计算它。
由于此包装类的作用也称为memoization,因此我选择调用模板Memo
,因为类型比CacheWrapper
更短,我开始更喜欢我年老时的名字较短。
#include <algorithm>
#include <map>
#include <utility>
#include <vector>
// An anonymous namespace to hold a search predicate definition. Users of
// Memo don't need to know this implementation detail, so I keep it
// anonymous. I use a predicate to search a vector of pairs instead of a
// simple map because a map requires that operator< be defined for its key
// type, and operator< isn't defined for member function pointers, but
// operator== is.
namespace {
template <typename Type1, typename Type2>
class FirstEq {
FirstType value;
public:
typedef std::pair<Type1, Type2> ArgType;
FirstEq(Type1 t) : value(t) {}
bool operator()(const ArgType& rhs) const {
return value == rhs.first;
}
};
};
template <typename T>
class Memo {
// Typedef for a member function of T. The C++ standard allows casting a
// member function of a class with one signature to a type of another
// member function of the class with a possibly different signature. You
// aren't guaranteed to be able to call the member function after
// casting, but you can use the pointer for comparisons, which is all we
// need to do.
typedef void (T::*TMemFun)(void);
typedef std::vector< std::pair<TMemFun, void*> > FuncRecords;
T memoized;
FuncRecords funcCalls;
public:
Memo(T t) : memoized(t) {}
template <typename ReturnType, typename ArgType>
ReturnType dispatch(ReturnType (T::* memFun)(ArgType), ArgType arg) {
typedef std::map<ArgType, ReturnType> Record;
// Look up memFun in the record of previously invoked member
// functions. If this is the first invocation, create a new record.
typename FuncRecords::iterator recIter =
find_if(funcCalls.begin(),
funcCalls.end(),
FirstEq<TMemFun, void*>(
reinterpret_cast<TMemFun>(memFun)));
if (recIter == funcCalls.end()) {
funcCalls.push_back(
std::make_pair(reinterpret_cast<TMemFun>(memFun),
static_cast<void*>(new Record)));
recIter = --funcCalls.end();
}
// Get the record of previous arguments and return values.
// Find the previously calculated value, or calculate it if
// necessary.
Record* rec = static_cast<Record*>(
recIter->second);
typename Record::iterator callIter = rec->lower_bound(arg);
if (callIter == rec->end() || callIter->first != arg) {
callIter = rec->insert(callIter,
std::make_pair(arg,
(memoized.*memFun)(arg)));
}
return callIter->second;
}
};
这是一个显示其用途的简单测试:
#include <iostream>
#include <sstream>
#include "Memo.h"
using namespace std;
struct C {
int three(int x) {
cout << "Called three(" << x << ")" << endl;
return 3;
}
double square(float x) {
cout << "Called square(" << x << ")" << endl;
return x * x;
}
};
int main(void) {
C c;
Memo<C> m(c);
cout << m.dispatch(&C::three, 1) << endl;
cout << m.dispatch(&C::three, 2) << endl;
cout << m.dispatch(&C::three, 1) << endl;
cout << m.dispatch(&C::three, 2) << endl;
cout << m.dispatch(&C::square, 2.3f) << endl;
cout << m.dispatch(&C::square, 2.3f) << endl;
return 0;
}
在我的系统上产生以下输出(使用g ++ 4.0.1的MacOS 10.4.11):
Called three(1) 3 Called three(2) 3 3 3 Called square(2.3) 5.29 5.29
void
,但如果方法没有返回任何内容,那么你不需要缓存结果!答案 3 :(得分:0)
答案 4 :(得分:0)
我还没有想出处理对象方法的情况,但我认为我对常规函数有一个很好的修复
template <typename input_t, typename output_t>
class CacheWrapper
{
public:
CacheWrapper (boost::function<output_t (input_t)> f)
: _func(f)
{}
output_t operator() (const input_t& in)
{
if (in != input_)
{
input_ = in;
output_ = _func(in);
}
return output_;
}
private:
boost::function<output_t (input_t)> _func;
input_t input_;
output_t output_;
};
将使用如下:
#include <iostream>
#include "CacheWrapper.h"
double squareit(double x)
{
std::cout << "computing" << std::endl;
return x*x;
}
int main (int argc, char** argv)
{
CacheWrapper<double,double> cached_squareit(squareit);
for (int i=0; i<10; i++)
{
std::cout << cached_squareit (10) << std::endl;
}
}
有关如何使其适用于对象的任何提示吗?