Home > Software engineering >  Using visitor pattern without writing too many overloads
Using visitor pattern without writing too many overloads

Time:11-17

I have two datatypes called DragonVector and UnbiasedDragon and I am using visitor pattern for dynamic type inference.

I want to extend a DragonVector only by a DragonVector and similarly for UnbiasedDragon.

I have the following code for extending the vectors:

template<class T>
class ExtendVisitor{
    public:
    void operator()(DragonVector<T>& vec1, const DragonVector<T>& vec2){
        vec1.extend(vec2);
    }
    void operator()(UnbiasedDragon<T>& vec1, const UnbiasedDragon<T>& vec2){
        vec1.extend(vec2);
    }
    void operator()(auto& vec1, const auto& vec2){
        std::cout<<"wrong class"<<std::endl;
    } 
};

I get error: 'auto' not allowed in function prototype. I am using C 17.

Since, there are only two classes, I can exhaustively write the operator overloads in the visitor for all the combinations. But this seems infeasible as the number of classes grow large.

I tried using templating as a work around as

template<class T>
class ExtendVisitor{
    public:
    void operator()(DragonVector<T>& vec1, const DragonVector<T>& vec2){
        vec1.extend(vec2);
    }
    void operator()(UnbiasedDragon<T>& vec1, const UnbiasedDragon<T>& vec2){
        vec1.extend(vec2);
    }
    template<class TT>
    void operator()(TT& vec1, const TT& vec2){
        std::cout<<"wrong class"<<std::endl;
    } 
};

but this also did not work out.

Is there a way to use visitor pattern without having to write all the possible combinations?

CodePudding user response:

You can make it a template to catch all other combinations. In your attempt your method has both arguments of same type, but you want different types:

template <typename A,typename B>
void operator()(A& vec1, const B& vec2){
    std::cout<<"wrong class"<<std::endl;
} 

Alternatively use lambdas as in the example here: https://en.cppreference.com/w/cpp/utility/variant/visit. Generic lambdas are available since C 14 already.

CodePudding user response:

The syntax in the first snippet is available starting with C 20 for which it would work as expected.

The second snippet doesn't work only because you are forcing the two arguments to be the same case in the default overload. There should be two template parameters, one for each function parameter.

There is however no need to make it this complicated anyway. You can use a single template and then implement your condition (that the types are equal) in its body:

template<typename T, typename U>
void operator()(T& t, const U& u){
    static_assert(!std::is_const_v<T>, "ExtendVisitor requires the left-hand to be modifiable!");

    if constexpr(std::is_same_v<T, U>) {
        vec1.extend(vec2);
    } else {
        // You should probably throw an exception here instead!
        std::cout<<"wrong class"<<std::endl;
    }
}
  • Related