简体   繁体   中英

creating type vector in c++

I have several classes that each of them has an ID and the Id is passed to the class as a template parameter:

typedef class1<1> baseClass;
typedef class2<2> baseClass;

typedef class<100> baseClass;

Now I need a map so if I can associate 1 with Class1 and 2 with Class2 and so on.

How can I create such vector? I am working on a header only library, so it should be a header only definition.

I am looking something that do the same thing that this code would do (if someone can compile it!):

std::map<int,Type> getMap()
{
      std::map<int,Type> output;
      output.add(1,class1);
      output.add(2,class2);

      output.add(100,class100);
}

The idea is that when I get as input 1, I create a class1 and when I receive 2, I create class2.

Any suggestion is very appreciated.

using this data, then I can write a function like this:

void consume(class1 c)
{
    // do something interesting with c
} 
void consume(class2 c)
{
    // do something interesting with c
} 
void consume(class3 c)
{
    // do something interesting with c
} 

void consume(int id,void * buffer)
{
    auto map=getMap();
    auto data= new map[id](buffer); // assuming that this line create a class  based on map, so the map provide the type that it should be created and then this line create that class and pass buffer to it.
     consume(data);
 }     

As a sketch:

class BaseClass { virtual ~BaseClass() = default; };

template<std::size_t I>
class SubClass : public BaseClass {};

namespace detail {
    template<std::size_t I>
    std::unique_ptr<BaseClass> makeSubClass() { return { new SubClass<I> }; }

    template<std::size_t... Is>
    std::vector<std::unique_ptr<BaseClass>(*)> makeFactory(std::index_sequence<Is...>)
    { return { makeSubclass<Is>... }; }
}

std::vector<std::unique_ptr<BaseClass>(*)> factory = detail::makeFactory(std::make_index_sequence<100>{});

We populate the vector by expanding a parameter pack, so we don't have to write out all 100 instantiations by hand. This gives you Subclass<0> at factory[0] , Subclass<1> at factory[1] , etc. up to Subclass<99> at factory[99] .

If I understand correctly you want a map to create different types according to a given number.

If that is so, then the code should look something like this:

class Base
{
};

template <int number>
class Type : public Base
{
public:
    Type()
    {
        std::cout << "type is " << number << std::endl;
    }

};

using Type1 = Type<1>;
using Type2 = Type<2>;
using Type3 = Type<3>;

using CreateFunction = std::function<Base*()>;
std::map<int, CreateFunction> creators;

int main()
{       
    creators[1] = []() -> Base* { return new Type1(); };
    creators[2] = []() -> Base* { return new Type2(); };
    creators[3] = []() -> Base* { return new Type3(); };

    std::vector<Base*> vector;
    vector.push_back(creators[1]());
    vector.push_back(creators[2]());
    vector.push_back(creators[3]());

}

output:

type is 1
type is 2
type is 3

If you need only to create object, it would be enough to implement template creator function like:

template<int ID>
Base<ID> Create()
{
    return Base<ID>();
}

And then use it:

auto obj1 = Create<1>();
auto obj2 = Create<2>();
// etc

Working example: https://ideone.com/urh7h6

Due to C++ being a statically-typed language, you may choose to either have arbitrary types that do a fixed set of things or have a fixed set of types do arbitrary things, but not both.

Such limitations is embodied by std::function and std::variant . std::function can have arbitrary types call operator() with a fixed signature, and std::variant can have arbitrary functions visit the fixed set of types.

Since you already said the types may be arbitrary, you may only have a fixed set of things you can do with such a type (eg consume ). The simplest way is to delegate the hard work to std::function

struct Type
{
    template<typename T>
    Type(T&& t)
      : f{[t = std::forward<T>(t)]() mutable { consume(t); }} {}
    std::function<void()> f;
};

void consume(Type& t)
{
    t.f();
}

What you are looking for is either the Stategy pattern:

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

class A {
public:
  A() {}
  virtual void doIt() {};
};

class Aa : public A {
public:
  Aa() {}
  virtual void doIt() {
    std::cout << "do it the Aa way" << std::endl;
  }
};

class Ab : public A {
public:
  Ab() {}
  virtual void doIt() {
    std::cout << "do it the Ab way" << std::endl;
  }
};

class Concrete {
public:
  Concrete(std::string const& type) {
    if (type == ("Aa")) { 
      _a.reset(new Aa());
    } else if (type == "Ab") {
      _a.reset(new Ab());
    }
  }
  void doIt () const {
    _a->doIt();
  }
private:
  std::unique_ptr<A> _a;
};

int main() {
  std::vector<Concrete> vc;
  vc.push_back(Concrete("Aa"));
  vc.push_back(Concrete("Ab"));

  for (auto const& i : vc) {
    i.doIt();
  }
  return 0;
}

Will output:

do it the Aa way
do it the Ab way

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM