2011-01-14 23 views
12

我有一些遗留代码,而不是虚函数,使用kind字段做动态调度。它看起来是这样的:没有虚函数的C++动态调度

// Base struct shared by all subtypes 
// Plain-old data; can't use virtual functions 
struct POD 
{ 
    int kind; 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 
}; 

enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ }; 

struct Derived1: POD 
{ 
    Derived1(): kind(Kind_Derived1) {} 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 

    // ... plus other type-specific data and function members ... 
}; 

struct Derived2: POD 
{ 
    Derived2(): kind(Kind_Derived2) {} 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 

    // ... plus other type-specific data and function members ... 
}; 

struct Derived3: POD 
{ 
    Derived3(): kind(Kind_Derived3) {} 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 

    // ... plus other type-specific data and function members ... 
}; 

// ... and so on for other derived classes ... 

,然后POD类的成员函数都是这样实现的:

int POD::GetFoo() 
{ 
    // Call kind-specific function 
    switch (kind) 
    { 
    case Kind_Derived1: 
     { 
     Derived1 *pDerived1 = static_cast<Derived1*>(this); 
     return pDerived1->GetFoo(); 
     } 
    case Kind_Derived2: 
     { 
     Derived2 *pDerived2 = static_cast<Derived2*>(this); 
     return pDerived2->GetFoo(); 
     } 
    case Kind_Derived3: 
     { 
     Derived3 *pDerived3 = static_cast<Derived3*>(this); 
     return pDerived3->GetFoo(); 
     } 

    // ... and so on for other derived classes ... 

    default: 
     throw UnknownKindException(kind, "GetFoo"); 
    } 
} 

POD::GetBar()POD::GetBaz()POD::GetXyzzy(),和其他成员也同样实施。

这个例子被简化了。实际的代码有大约十二种不同的子类型POD,以及几十种方法。 POD的新子类型和新方法会相当频繁地添加,所以每次我们这样做时,都必须更新所有这些switch语句。

处理此问题的典型方法是在POD类中声明函数成员virtual,但我们不能这样做,因为对象驻留在共享内存中。有很多代码依赖于这些结构是普通的旧数据,所以即使我可以想出一些方法来在共享内存对象中使用虚函数,我也不想这样做。

所以,我正在寻找最好的方法来清理这个问题,以便所有关于如何调用子类型方法的知识都集中在一个地方,而不是分散在几十个switch语句中几十个功能。

发生在我身上的是我可以创建某种类型的适配器类来包装POD并使用模板来最小化冗余。但在我开始这条道路之前,我想知道其他人是如何处理这一问题的。

+0

你说有很多的代码取决于这个类。你可以添加字段吗?还是结构需要保持不变? – 2011-01-14 16:35:09

+0

结构应该保持基本相同。我们在共享内存中有大量这些东西,并且已经碰到内存大小的限制。 – 2011-01-14 16:44:04

+0

是否所有的多个进程都具有完全相同的库版本,或者不是? – 2011-01-14 17:48:08

回答

11

您可以使用跳转表。这是大多数虚拟调度在外观下的样子,您可以手动构建它。

template<typename T> int get_derived_foo(POD*ptr) { 
    return static_cast<T>(ptr)->GetFoo(); 
} 
int (*)(POD*) funcs[] = { 
    get_derived_foo<Derived1>, 
    get_derived_foo<Derived2>, 
    get_derived_foo<Derived3> 
}; 
int POD::GetFoo() { 
    return funcs[kind](this); 
} 

举一个简短的例子。

共享内存的限制究竟是什么?我意识到我在这里不够了解。这是否意味着我不能使用指针,因为另一个进程中的某个人将尝试使用这些指针?

您可以使用一个字符串映射,其中每个进程获取它自己的映射副本。你必须将它传递给GetFoo()以便它能够找到它。

struct POD { 
    int GetFoo(std::map<int, std::function<int()>& ref) { 
     return ref[kind](); 
    } 
}; 

编辑:当然,你不必在这里使用字符串,你可以使用int。我只是用它作为例子。我应该改回它。事实上,这个解决方案非常灵活,但重要的是,制作特定于流程的数据的副本,例如函数指针或任何东西,然后将其传入。

0

下面是一个使用奇怪的循环模板模式的例子。如果您在编译时了解更多信息,这可能适合您的需求。

template<class DerivedType> 
struct POD 
{ 
    int GetFoo() 
    { 
     return static_cast<DerivedType*>(this)->GetFoo(); 
    } 
    int GetBar() 
    { 
     return static_cast<DerivedType*>(this).GetBar(); 
    } 
    int GetBaz() 
    { 
     return static_cast<DerivedType*>(this).GetBaz(); 
    } 
    int GetXyzzy() 
    { 
     return static_cast<DerivedType*>(this).GetXyzzy(); 
    } 
}; 

struct Derived1 : public POD<Derived1> 
{ 
    int GetFoo() 
    { 
     return 1; 
    } 
    //define all implementations 
}; 

struct Derived2 : public POD<Derived2> 
{ 
    //define all implementations 

}; 

int main() 
{ 
    Derived1 d1; 
    cout << d1.GetFoo() << endl; 
    POD<Derived1> *p = new Derived1; 
    cout << p->GetFoo() << endl; 
    return 0; 
} 
1

这是一种使用虚拟方法实现跳转表的方法,无需Pod类或派生类实际具有虚函数。

目标是简化跨许多类的添加和删除方法。

要添加一个方法,它需要使用清晰和通用的模式添加到Pod,纯虚函数需要添加到PodInterface,并且必须使用清晰和通用的模式将转发功能添加到PodFunc。

派生类只需要一个文件静态初始化对象来设置事物,否则看起来很像他们已经做的事情。

// Pod header 

#include <boost/shared_ptr.hpp> 
enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ }; 

struct Pod 
{ 
    int kind; 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
}; 

struct PodInterface 
{ 
    virtual ~PodInterface(); 

    virtual int GetFoo(Pod* p) const = 0; 
    virtual int GetBar(Pod* p) const = 0; 
    virtual int GetBaz(Pod* p) const = 0; 

    static void 
    do_init(
      boost::shared_ptr<PodInterface const> const& p, 
      int kind); 
}; 

template<class T> struct PodFuncs : public PodInterface 
{ 
    struct Init 
    { 
     Init(int kind) 
     { 
      boost::shared_ptr<PodInterface> t(new PodFuncs); 
      PodInterface::do_init(t, kind); 
     } 
    }; 

    ~PodFuncs() { } 

    int GetFoo(Pod* p) const { return static_cast<T*>(p)->GetFoo(); } 
    int GetBar(Pod* p) const { return static_cast<T*>(p)->GetBar(); } 
    int GetBaz(Pod* p) const { return static_cast<T*>(p)->GetBaz(); } 
}; 


// 
// Pod Implementation 
// 

#include <map> 

typedef std::map<int, boost::shared_ptr<PodInterface const> > FuncMap; 

static FuncMap& get_funcmap() 
{ 
    // Replace with other approach for static initialisation order as appropriate. 
    static FuncMap s_funcmap; 
    return s_funcmap; 
} 

// 
// struct Pod methods 
// 

int Pod::GetFoo() 
{ 
    return get_funcmap()[kind]->GetFoo(this); 
} 

// 
// struct PodInterface methods, in same file as s_funcs 
// 

PodInterface::~PodInterface() 
{ 
} 

void 
PodInterface::do_init(
     boost::shared_ptr<PodInterface const> const& p, 
     int kind) 
{ 
    // Could do checking for duplicates here. 
    get_funcmap()[kind] = p; 
} 

// 
// Derived1 
// 

struct Derived1 : Pod 
{ 
    Derived1() { kind = Kind_Derived1; } 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 

    // Whatever else. 
}; 

// 
// Derived1 implementation 
// 

static const PodFuncs<Derived1>::Init s_interface_init(Kind_Derived1); 

int Derived1::GetFoo() { /* Implement */ } 
int Derived1::GetBar() { /* Implement */ } 
int Derived1::GetBaz() { /* Implement */ } 
1

这里是模板元编程的路径,我现在要走了。以下是我喜欢的内容:

  • 添加对新类型的支持只需要更新LAST_KIND并添加新的KindTraits
  • 添加新功能有一个简单的模式。
  • 如有必要,函数可以专用于特定种类。
  • 如果我搞砸了,我可以期待编译时错误和警告,而不是神秘的运行时错误行为。

有一对夫妇的忧虑:

  • POD的实施现在取决于所有派生类的接口。 (这在现有的实现中已经是真实的,所以我并不担心它,但它有点味道。)
  • 我指望编译器足够聪明以生成大致相当的代码基于switch的代码。
  • 许多C++程序员在看到这些时会搔头。

下面的代码:

// Declare first and last kinds 
const int FIRST_KIND = Kind_Derived1; 
const int LAST_KIND = Kind_Derived3; 

// Provide a compile-time mapping from a kind code to a subtype 
template <int KIND> 
struct KindTraits 
{ 
    typedef void Subtype; 
}; 
template <> KindTraits<Kind_Derived1> { typedef Derived1 Subtype; }; 
template <> KindTraits<Kind_Derived2> { typedef Derived2 Subtype; }; 
template <> KindTraits<Kind_Derived3> { typedef Derived3 Subtype; }; 

// If kind matches, then do the appropriate typecast and return result; 
// otherwise, try the next kind. 
template <int KIND> 
int GetFooForKind(POD *pod) 
{ 
    if (pod->kind == KIND) 
     return static_cast<KindTraits<KIND>::Subtype>(pod)->GetFoo(); 
    else 
     return GetFooForKind<KIND + 1>(); // try the next kind 
} 

// Specialization for LAST_KIND+1 
template <> int GetFooForKind<LAST_KIND + 1>(POD *pod) 
{ 
    // kind didn't match anything in FIRST_KIND..LAST_KIND 
    throw UnknownKindException(kind, "GetFoo"); 
} 

// Now POD's function members can be implemented like this: 

int POD::GetFoo() 
{ 
    return GetFooForKind<FIRST_KIND>(this); 
} 
0

扩展在你结束了解决,下面解决映射到派生功能在程序初始化:

#include <typeinfo> 
#include <iostream> 
#include <functional> 
#include <vector> 

enum Kind 
{ 
    Kind_First, 
    Kind_Derived1 = Kind_First, 
    Kind_Derived2, 
    Kind_Total 
}; 

struct POD 
{ 
    size_t kind; 

    int GetFoo(); 
    int GetBar(); 
}; 

struct VTable 
{ 
    std::function<int(POD*)> GetFoo; 
    std::function<int(POD*)> GetBar; 
}; 

template<int KIND> 
struct KindTraits 
{ 
    typedef POD KindType; 
}; 

template<int KIND> 
void InitRegistry(std::vector<VTable> &t) 
{ 
    typedef typename KindTraits<KIND>::KindType KindType; 

    size_t i = KIND; 
    t[i].GetFoo = [](POD *p) -> int { 
     return static_cast<KindType*>(p)->GetFoo(); 
    }; 
    t[i].GetBar = [](POD *p) -> int { 
     return static_cast<KindType*>(p)->GetBar(); 
    }; 

    InitRegistry<KIND+1>(t); 
} 
template<> 
void InitRegistry<Kind_Total>(std::vector<VTable> &t) 
{ 
} 

struct Registry 
{ 
    std::vector<VTable> table; 

    Registry() 
    { 
     table.resize(Kind_Total); 
     InitRegistry<Kind_First>(table); 
    } 
}; 

Registry reg; 

int POD::GetFoo() { return reg.table[kind].GetFoo(this); } 
int POD::GetBar() { return reg.table[kind].GetBar(this); } 

struct Derived1 : POD 
{ 
    Derived1() { kind = Kind_Derived1; } 

    int GetFoo() { return 0; } 
    int GetBar() { return 1; } 
}; 
template<> struct KindTraits<Kind_Derived1> { typedef Derived1 KindType; }; 

struct Derived2 : POD 
{ 
    Derived2() { kind = Kind_Derived2; } 

    int GetFoo() { return 2; } 
    int GetBar() { return 3; } 
}; 
template<> struct KindTraits<Kind_Derived2> { typedef Derived2 KindType; }; 

int main() 
{ 
    Derived1 d1; 
    Derived2 d2; 
    POD *p; 

    p = static_cast<POD*>(&d1); 
    std::cout << p->GetFoo() << '\n'; 
    p = static_cast<POD*>(&d2); 
    std::cout << p->GetBar() << '\n'; 
}