C ++访问者模式处理模板化的字符串类型?

时间:2010-03-31 19:02:11

标签: c++ design-patterns visitor

我正在尝试使用访问者模式来序列化对象的内容。然而,当我正在访问字符串时,我遇到的一个障碍。我的字符串是模板化类型,类似于STL的basic_string。如下所示:

basic_string<char_type, memory_allocator, other_possible_stuff> \\ many variations possible!

由于我可以拥有很多不同的模板化字符串类型,因此我无法将它们添加到访问者界面中。这太荒谬了。但我无法将模板添加到我的VisitString方法,因为C ++阻止在虚拟方法中使用模板参数。

那么我可以选择解决这个问题?

编辑:我添加了一些基本代码

class IVisitor
{
public: 
     virtual void VisitString(some_kind_of_string_type string) = 0; // this is what I want in theory
};

class MyObject
{
public:
    typedef basic_string<char8, myAllocator, some_flag> MyStringType;
    Accept(IVisitor* visitor)
    {
        visitor->VisitString(mString); 
    }
private:
   MyStringType string;
};

class MyOtherObject
{
public:
    typedef basic_string<char16, myOtherAllocator, some_other_flag> MyOtherStringType;
    Accept(IVisitor* visitor)
    {
        visitor->VisitString(mString); 
    }
private:
   MyOtherStringType string;
};


class Reader : public IVisitor
{ 
public:
    virtual void VisitString(some_kind_of_string_type string)
    {
         // read some data, give it to the string
    }
}

7 个答案:

答案 0 :(得分:1)

您是否需要运行时多态性?

struct object {
   template <typename Visitor>
   void accept( Visitor & v )
   {
      v( x );
      v( a );
   }

   int x;
   std::string a;
};
struct complex_object {
   template <typename Visitor>
   void accept( Visitor & v ) {
      v( i );
      o.accept(v); // [1]
   }
   int i;
   object1 o;
};

struct DumpScreenVisitor {
   void operator()( int x ) { std::cout << x << std::endl; }
   template <typename char_t, typename traits_t, typename alloc_t>
   void operator()( std::basic_string<char_t, traits_t, alloc_t> const & str )
   {
      std::cout << str << std::endl;
   }
};

[1]中的调用可以转换为v( o ),其中最常用的访问者中使用通用模板化operator()

template <typename O>
void DumpScreenVisitor::operator()( O & o )
{
   o.accept( *this );
}

但是这可以与其他访问者实现相互干扰(例如,上面的访问者可以使用单个模板化方法实现):

struct DumpScreenVisitor {
   template <typename T>
   void operator()( T const & t ) {
      std::cout << t << std::endl;
   }
};

所以最后你必须以任何一种方式妥协。

这种方法类似于boost :: variant访问者实现(您可能需要查看它),区别在于boost :: variant是单个类而不是层次结构。

答案 1 :(得分:1)

最后,我采用了略微不同的方法。我决定将一个类似访问者的类作为模板参数传递给我的对象的访问方法,而不是希望使用模板化方法的访问者(当然,这是不可能的)。完全简化的例子:

class SomeKindOfVisitor // doesn't need to derive from a base class. 
{
     template <class StringClass>
     void VisitString(StringClass& string) // I get to keep templated methods
}


class MyObject
{
typedef basic_string<char8, myAllocator, some_flag> MyStringType;

public:

   template <class VisitorClass>
   void Accept(VisitorClass& visitor)
   {
       vistior.VisitString<MyStringType>(mMyString);
   }
private:
    MyStringType mMyString;
}

使用这种方法,我仍然可以使用我的模板化字符串,同时仍然能够将任何类型的“访问者”传递给我的对象。

答案 2 :(得分:0)

嗯,问题是,你的字符串上的模板参数可能有很大不同,你能为它们应用一个单一的序列化方法吗?如果是这样,您可以编写一个具有模板化构造函数的适配器,该构造函数将序列化所需的所有信息提取到统一表示中。然后使用适配器访问序列化程序。

编辑:在您添加代码之后,我仍然认为适配器可以解决您的问题,只是相反。在您Accept - 方法中,构造一个本地适配器并将其传递给Visitor。当Visitor修改它时,您可以在适配器上使用模板方法extractToString,将信息转换为特定的字符串版本。这可能会使适配器退出复杂,具体取决于必须处理字符串模板实例的不同。

答案 3 :(得分:0)

您的访问者应该只处理字符串的基本表示(char * / wchar *);

然后由accept方法来处理演员表。

答案 4 :(得分:0)

由于您的所有字符串类都是不同类型的,因此您需要一定程度的折衷(通用子类型,虚拟方法,字符串,或适配器,或为每个不同添加方法键入访问者)。混合泛型编程和oo可能会很痛苦,特别是如果你不接受妥协的话。

例如

class string_tag { /* common visitor interface */ };

template<typename char_t, ...> class basic_string : public string_tag {};

class IVisitor
{
public: 
     virtual void VisitString(string_tag& string) = 0; // this is what I want in theory
};

class MyObject
{
public:
    typedef basic_string<char8, myAllocator, some_flag> MyStringType;
    Accept(IVisitor* visitor)
    {
        visitor->VisitString(string); 
    }
private:
   MyStringType string;
};

class MyOtherObject
{
public:
    typedef basic_string<char16, myOtherAllocator, some_other_flag> MyOtherStringType;
    Accept(IVisitor* visitor)
    {
        visitor->VisitString(string); 
    }
private:
   MyOtherStringType string;
};


class Reader : public IVisitor
{ 
public:
    virtual void VisitString(string_tag& string)
    {
         // read some data, give it to the string
    }
}

答案 5 :(得分:0)

您可以在下面考虑,但在这种情况下,您需要将访问者机制分隔到不同的访客类别。 WStringVisitor和StringVisitor只是不同Visitor语义的示例。

#include <string>

#include <iostream>

using namespace std;

template <typename stringType>
class IVisitor{
public:
    virtual void visit(stringType _string)=0;
};

class StringVisitor: public IVisitor<string>{
public:
    void visit(string str){
        cout<<"This is std::string implementation: "<< str << endl;
    }
};
class WStringVisitor: public IVisitor<basic_string<wchar_t>>{
public:
    void visit(basic_string<wchar_t> str){
        //wprintf(L"This wide implementation : %S", str.c_str());
        wcout<<"This is WString Visitor: "<< str << endl;
    }
};

class MyObject{
public:
    typedef basic_string<char> MyStringType;

    void accept(IVisitor<MyStringType>& visitor){
        visitor.visit("TEST STRING");
    }
};

class MyOtherObject
{
public:
    typedef basic_string<wchar_t> MyOtherStringType;
    void accept(IVisitor<MyOtherStringType>& visitor)
    {
        visitor.visit(L"TEST WSTRING"); 
    }

};


int _tmain(int argc, _TCHAR* argv[])
{
    MyObject acceptor;
    MyOtherObject otheracceptor;
    StringVisitor visitor;
    WStringVisitor wvisitor;
    acceptor.accept(visitor);
    //otheracceptor.accept(visitor); compile error
    otheracceptor.accept(wvisitor);
    return 0;
} 

答案 6 :(得分:0)

我认为这里的根本问题是访问者模式是关于虚拟功能的,而你通过功能模板将你的字符串放在一边。而这些只是不容易混合。事实上,我认为混合两者的唯一方法是type erasure

如果您没有找到使用此技术做任何事情的方法,我认为您不会找到方法。