1

我正在尝试使用访问者模式来序列化对象的内容。然而,我遇到的一个障碍是当我访问字符串时。我的字符串是模板类型,类似于 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
    }
}
4

7 回答 7

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 是单个类而不是层次结构。

于 2010-03-31T20:19:25.343 回答
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;
}

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

于 2010-04-06T14:19:20.080 回答
0

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

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

于 2010-03-31T19:11:01.000 回答
0

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

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

于 2010-03-31T19:24:52.497 回答
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
    }
}
于 2010-03-31T19:51:17.497 回答
0

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

#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;
} 
于 2010-04-02T10:57:42.913 回答
0

我认为这里的基本问题是访问者模式都是关于虚拟函数的,而你通过函数模板来聚集你的字符串。而这些只是不容易混合。事实上,我能想到的将两者混合的唯一方法是type erasure

如果你找不到使用这种技术做你想做的事的方法,我认为你不会找到方法。

于 2010-04-06T08:06:09.740 回答