-1

I have a templated Prob class that acts as a way to organize various programming problems from different problem sets. The template is the problem number. How would I go about storing different Prob objects in a vector or map?

Here is the class declaration:

template<int ProbNum>
class Prob 
{
    std::string 
    text(std::ostream& out) 
    {
        out << "Prob" << ((ProbNum < 10) ? "0" : "") << ProbNum << ": "; 
    }

    void solve(std::ostream& out);
};

So in other words if I want to declare an object for problem 1 of some problem set I would do

Prob<1> p1;

and then store that in a map or vector for later use so the user can call on it during runtime (since you cannot pass a runtime argument into a template).

Edit: I want to use this class as an abstract class for other Prob classes.

Edit2: Added more code for clarification.

Edit3: Top half is Prob1.hpp

Bottom half is a driver file on how I want to use it.

#include <iostream>

#include "Prob.hpp"

template<>
void
Prob<1>::solve(std::ostream& out)
{
    out << "solution to prob1";
}

/***************************************************/

#include <iostream>
#include <cstdlib>

#include "Prob.hpp"

// Finished Problems
#include "Prob1.hpp"

int
main(int argc, char* argv[])
{
    Prob<1> p;
    p.solve(std::cout);
}
sfmalloy
  • 13
  • 2
  • 4
    Does the problem number really need to be a template? If you just made it a class member it would be trivial to store `Prob`'s in a container. – NathanOliver Nov 29 '18 at 21:49
  • @NathanOliver I probably should have mentioned this but I want to use this as an abstract class so I can have many files with generic Prob functions and then the specific solution. – sfmalloy Nov 29 '18 at 21:51
  • No STL container is suited to store heterogeneous types. If you have to do this, you'd have to use some form of type erasure. – SergeyA Nov 29 '18 at 21:52
  • You can still do that by making the problem number member of `Prob`. I'm not seeing any benefit of making it a template parameter – NathanOliver Nov 29 '18 at 21:53
  • @NathanOliver I think I see what you mean. The only reason I wanted to make it templated is so I can have seperate files and not have to name the classes all Prob1, Prob2, Prob3 etc. But I guess there is still a way to access the member functions of Prob without a template? – sfmalloy Nov 29 '18 at 21:54
  • @Sean Why do problems need to be different types at all? The problem number/description can be part of the value of the class. –  Nov 29 '18 at 21:56
  • I can see I am probably going about this whole thing the wrong way. I'll try and rethink this. Thank you all for your input – sfmalloy Nov 29 '18 at 21:58
  • We still do not see code for `solve`. – SergeyA Nov 29 '18 at 21:58
  • @Sean I would need to see more context on how you actually want to use this. Could you pseudo code how you would like it to work? – NathanOliver Nov 29 '18 at 21:58
  • @SeregyA I wanted solve to be different for each problem. I am trying to make an abstract class and each templated problem would implement solve differently. – sfmalloy Nov 29 '18 at 21:59
  • But there is no abstract class in your example. Abstract class is a class with at least one pure virtual function. I see none. – SergeyA Nov 29 '18 at 22:00
  • @NathanOliver Yes I will add some more context. – sfmalloy Nov 29 '18 at 22:00
  • @SergeyA I'll need to do some more research then I am still kind of new to C++ (Obviously haha). – sfmalloy Nov 29 '18 at 22:10
  • @Sean It seems to me something like [this](http://coliru.stacked-crooked.com/a/a57b314496df4d0d) is what you need. – NathanOliver Nov 29 '18 at 22:16

1 Answers1

0

Each instance of a template class constitutes a different type. Hence, containers like std::vector cannot hold Prob<ProbNum> for different values of ProbNum. If you know at compile time the number of Prob<ProbNum> instances you want, and the corresponding values of the template parameter int ProbNum you could store everything into a tuple. For example:

auto mycollection = std::make_tuple(Prob<1>(), Prob<2>());

A more general solution could be to define an abstract base class for Prob. Then you can manage to store a vector of Prob<ProbNum> objects, with inhomogeneous values of int ProbNum, if you define a vector of pointers to the base class. For this to work you must provide the interface in the base class, i.e., every member of Prob<ProbNum> that you want to access through the vector of the base class, must be virtual and already declared in the base class.

Consider the following example:

#include <iostream>
#include <memory>
#include <vector>

struct base {
  virtual void print() const = 0;
  virtual ~base() = default;
};

template <int i>
struct derived : public base
{
  virtual void print() const { std::cout << i << std::endl; }
};

int main()
{
  std::vector<std::unique_ptr<base>> vec;
  vec.emplace_back(new derived<1>());
  vec.emplace_back(new derived<3>());
  vec.emplace_back(new derived<5>());
  for (auto& el : vec)
    el->print();

  return 0;  
}

The variable vec is essentially a vector of pointers to objects of type derived<i>, with inhomogeneous values of i. Because base::print() is virtual, it correctly resolves to the corresponding method of the derived<i> class. Notice that I used a smart pointer to avoid memory leaking.

Also, it is important to declare virtual the destructor of base, see the discussion Why should I declare a virtual destructor for an abstract class in C++?.

francesco
  • 7,189
  • 7
  • 22
  • 49
  • Thank you this is more along the lines of what I was looking for, just didn't give enough context at first. Thank you! – sfmalloy Nov 29 '18 at 22:23