rgkirch rgkirch - 2 months ago 15
C++ Question

Trying to apply the pattern from sean parent's talk "inheritance is the base class of evil"

https://ideone.com/1Pkxwe

I'm trying to make a for comprehension in c++. I want to construct a

For
instance out of some number of other iterable containers. Later I'll pass a function and the
For
will call the function on it's contents.

I haven't gotten past the heterogeneous vector yet but I'm imagining that

void f(int i, char c) {
cout << i << " " << c << endl;
}
For(1,2)('a','b').yield(f);


will print

1 a
1 b
2 a
2 b


The above is just pseudo code... This is what I have so far. I'm borrowing heavily from Sean Parents talk and that source may be found here.

https://raw.githubusercontent.com/boostcon/cppnow_presentations_2012/master/fri/value_semantics/value_semantics.cpp

The Model::print doesn't see the Object::print functions and I don't understand why. It seems similar to that linked source code and it works there...

error output from ideone

prog.cpp: In instantiation of ‘void Object::Model<T>::print() [with T = std::__cxx11::basic_string<char>]’:
prog.cpp:58:5: required from here
prog.cpp:26:22: error: no matching function for call to ‘Object::Model<std::__cxx11::basic_string<char> >::print(std::__cxx11::basic_string<char>&)’
print(data);
~~~~~^~~~~~
prog.cpp:25:18: note: candidate: void Object::Model<T>::print() [with T = std::__cxx11::basic_string<char>]
void print() {
^~~~~
prog.cpp:25:18: note: candidate expects 0 arguments, 1 provided
prog.cpp: In instantiation of ‘void Object::Model<T>::print() [with T = int]’:
prog.cpp:58:5: required from here
prog.cpp:26:22: error: no matching function for call to ‘Object::Model<int>::print(int&)’
print(data);
~~~~~^~~~~~
prog.cpp:25:18: note: candidate: void Object::Model<T>::print() [with T = int]
void print() {
^~~~~
prog.cpp:25:18: note: candidate expects 0 arguments, 1 provided


and the code

#include <vector>
#include <string>
#include <functional>
#include <iostream>

using namespace std;

struct Object {
template <typename T>
Object(T t) : model(new Model<T>(t)) {
cout << "construct object from T" << endl;
}
friend void print(string str) {
cout << str << endl;
}
friend void print(int i) {
cout << i << endl;
}
struct Concept {
virtual void print() =0;
};
template <typename T>
struct Model : Concept {
Model(T t) : data(t) {}
void print() {
print(data);
}
T data;
};
Concept *model;
};

struct For {
For() {
cout << "default construct For" << endl;
}
For(Object o) {
cout << "construct For from object and push back" << endl;
objects.push_back(o);
}
For operator()(Object o) {
cout << "push back object" << endl;
objects.push_back(o);
return *this;
}
void print() {
for(auto o : objects) {
o.model->print();
}
};
vector<Object> objects;
};

int main() {
auto heterogeneous = For(1)(string("hello"));
function<void(int)> f = [](int i)->void{ cout << i << endl; };
heterogeneous.print();
}


A million thanks!

Answer Source

A few changes to make it compile.

#include <vector>
#include <string>
#include <functional>
#include <iostream>

using namespace std;

template <typename T>
void print(T&&);      // <- so that Object::Model knows what you want.

struct Object {
    template <typename T>
    Object(T t) : model(new Model<T>(t)) {
        cout << "construct object from T" << endl;
    }
    friend void print(string str) {
        cout << str << endl;
    }
    friend void print(int i) {
        cout << i << endl;
    }
    struct Concept {
        virtual ~Concept() = default;   // <- you MUST have a virtual dtor
        virtual void print() =0;
    };
    template <typename T>
    struct Model : Concept {
        Model(T t) : data(t) {}
        void print() {
            ::print(data);   // <-- the :: because Model::print hid what you wanted to call
        }
        T data;
    };
    Concept *model;
};


struct For {   // I don't recall this from Sean's talk.  I don't think it 
               // works as intended.
    For() {
        cout << "default construct For" << endl;
    }
    For(Object o) {
        cout << "construct For from object and push back" << endl;
        objects.push_back(o);
    }
    For operator()(Object o) {
        cout << "push back object" << endl;
        objects.push_back(o);
        return *this;
    }
    void print() {
        for(auto o : objects) {
            o.model->print();
        }
    };
    vector<Object> objects;
};

// print functions for your types.

void print(const std::string& s)
{
    std::cout << s << "\n";
}

void print(const int& i)
{
    std::cout << i << "\n";
}

int main() {
    auto heterogeneous = For(1)(string("hello"));  // this adds [1,"hello"] to the model
                                                   // is this what you want?
    //function<void(int)> f = [](int i)->void{ cout << i << endl; };
    heterogeneous.print();
}

In Sean's talk, what is your Object class was the actual document class, and that's where the vector<> lived. From what I understand, your for class should be a vector of documents, but it cannot do this as written.