5

The purpose of my program is to create a list of data which i can visit with a set of static visitors while using static polymorphism in my class hierarchy.

I have created a hierarchy of classes utilizing static polymorphism through CRTP:

class VirtualBaseData {
public:    
    //someVirtualFunction
}

template<typename Derived>
class BaseData<Derived> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         static_cast<Derived*>(this)->accept(v);
    }
}

class DerivedBaseData1: BaseData<DerivedBaseData> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         //Specific implementation
    }    
}
class DerivedBaseData2: BaseData<DerivedBaseData> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         //Specific implementation
    }    
}

I want to store the DerivedBaseData in a contain for later being iterated through and visited.

int main(){
    std::vector<VirtualBaseData*> dataSet;
    dataSet.push_back(new DerivedBaseData1);
    dataSet.push_back(new DerivedBaseData2);
    for(auto it = fifth.begin(); it != fifth.end(); ++it){
        it->accept(); //Error: VirtualBaseData does not have a member function accept
    }
}

I am looking for at a way to couple my static visitors with my static polymorphism hierarchy. I am in need of a VirtualBaseData class in my static polymorphism which is not a template class in order to use the classes in containers. However, since i can not have the VirtualBaseData class be a template class, i am unable to create the appropriate static_cast to a derived class as done in the CRTPattern.

My question is: does anyone have a good strategy which would preserves my static polymorphism setup as well as a static visitor pattern?

For reference: I have implemented my static visitors as described on page 21-23 in http://hillside.net/plop/2006/Papers/Library/portableProgrammingPL.pdf

Kognido
  • 51
  • 3
  • "making 'accept' a virtual function, would be inappropriate as it needs a specified visitor to preserve the static visitor portion of my program." This make little sense without context. Can you show what you mean by demonstrating a specific problem or error? In general once you pushed pointers to derived objects into a container of base pointers, you have erased the types and the only way to get them back is to call a virtual function (dynamic cast doesn't count because of problems with scalability). – n. m. could be an AI Jun 05 '16 at 16:27
  • I have rephrased the question a bit as to clearify. I have a set of static visitors which i want to pass to the 'accept' function in order to have the visitor perform some computations in the object. This could be achieved through templating the class and calling the derived class' accept function. However i also want to iterate through a set of the data objects. In order to do this while still preserving my static polymorphism hierarchy i have implemented the vbd(virtualbasedata) class. As such i can't make the vbd class a template class as i do not which to iterate through template classes. – Kognido Jun 05 '16 at 16:56
  • When types are not known statically, as is the case with a container of base class pointers, there's little chance to do static polymorphism. A plain old runtime polymorphism a.k.a. OOP is in order. This doesn't preclude static polymorphism in other parts of the program. – n. m. could be an AI Jun 05 '16 at 17:19

3 Answers3

2

If you don't know how many/what types your objects will be in compile time, then it's a use-case for dynamic polymorphism (at least, I don't know how to do it using only static polymorphism).

However... if you know at compile time exact number and types of your objects, now we're talking! Here's a minimal compiling example (code on ideone):

#include <iostream>
#include <tuple>
#include <type_traits>
using namespace std;


template<typename Derived>
class BaseData {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         static_cast<Derived*>(this)->accept(v);
    }
};

class DerivedBaseData1: BaseData<DerivedBaseData1> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
        std::cout << "DerivedBaseData1: accepting visitor " << v << std::endl;
    }    
};
class DerivedBaseData2: BaseData<DerivedBaseData2> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
        std::cout << "DerivedBaseData2: accepting visitor " << v << std::endl;
    }    
};

namespace impl {

    template <size_t N> 
    struct num2type {};

    template <size_t Idx, typename T, typename Visitor>
    void accept_impl(Visitor &v, T &&collection, num2type<Idx>) {
        // run accept on current object
        auto &object = std::get<Idx>(collection);
        object.accept(v);
        // move iteration forward
        accept_impl(v, std::forward<T>(collection), num2type<Idx - 1>{});
    }

    template <typename T, typename Visitor>
    void accept_impl(Visitor &v, T &&collection, num2type<0>) {
        // run accept on current object
        auto &object = std::get<0>(collection);
        object.accept(v);
    }
}

template<typename ...Ts, typename Visitor>
void accept(Visitor &v, std::tuple<Ts...> &&collection) {
    using T = decltype(collection);
    impl::accept_impl(v, std::forward<T>(collection), impl::num2type<std::tuple_size<std::decay_t<T>>::value - 1>{});
}


int main() {
    using visitor_type = int;
    visitor_type visitor = 42;

    DerivedBaseData1 a1, a3;
    DerivedBaseData2 a2;
    accept(visitor, std::tie(a1, a2, a3));

    return 0;
}

Using static polymorphism you can iterate a static collection (here, a std::tuple) and call desired method with desired arguments on each of them.

pzelasko
  • 2,082
  • 1
  • 16
  • 24
0

error is correct, because you creating vector of VirtualBaseData pointers. Class VirtualBaseData doesn't contain accept() function.

yout answer is: Can a C++ class member function template be virtual?

also read about polymorphism: http://www.cplusplus.com/doc/tutorial/polymorphism/

Community
  • 1
  • 1
goldstar
  • 327
  • 4
  • 15
  • Thanks for the quick response. I can however see that my question have been misunderstood, so ill be rephrasing here and in the original question. I am looking for at a way to couple my static visitors with my static polymorphism hierarchy. I am in need of a VirtualBaseData class in my static polymorphism which is not a template class in order to use the classes in containers. However, since i can not have the VirtualBaseData class be a template class, i am unable to create the appropriate static_cast to a derived class as done in the CRTPattern. – Kognido Jun 05 '16 at 16:27
0

As you said there is no simple solution, but you might want to use runtime polymorphism instead of static. In order to archive runtime polymorphism you need to perform type erasure on base class and dispatch to proper non-temaplte functions. This can be archived for example with boost::any from boost library. Constrain for boost::any is that all objects stored as boost::any must be copyable. If boost::any is not suitable for you read more about type erasuring.

class VirtualBaseData {
public:    
    template <typename T>
    void accept(T& visitor) {
        acceptImpl(boost::any(visitor));
    }
protected:
    virtual void acceptImpl(boost::any const & value ) = 0;
}
paweldac
  • 1,144
  • 6
  • 11
  • Hmm, i'll try this implementation out, however it would kind of defeat the purpose if i had to do runtime polymorphism in this particular part of the program. – Kognido Jun 05 '16 at 16:58
  • I don't think it's doable(at least I don't know any possible solution) with static polymorphism. – paweldac Jun 05 '16 at 17:00
  • With this implementation, how would i go about calling the correct visit function? Consider that i have 3+ visitor classes which could be passed to the accept function. Inside the accept function i wish to call the visitors visit function however i don't know which visitor i am currently operating with. To further complicate things, as part of them being static visitors, they do not share a parent. – Kognido Jun 05 '16 at 17:26
  • if they are being static visitors then just pass std::function as a argument and no template is needed(if I get correctly what you mean by static visitor). In order to differ those visitors I'd create BaseVisitor class, which wraps `boost::any` as member and another member with which you'll be able to differ those visitors, string for example or enum(this will be less flexible). When accepting visitor get his type identifier and handle each visitor in proper way. In order to get away from multiple if-else statements use map with visitor id and binded handler. – paweldac Jun 05 '16 at 17:33