简体   繁体   English

在 C++ 中创建动态类型

[英]Creating dynamic type in C++

I'm writing a piece of generic software that will be loaded on to many different variants of the same basic hardware.我正在编写一个通用软件,它将加载到相同基本硬件的许多不同变体上。 They all have the same processor, but with different peripherals and their own functions that need to be carried out.它们都具有相同的处理器,但具有不同的外设和各自需要执行的功能。 The software will know which variant it should run by reading a hardware switch value.软件将通过读取硬件开关值知道它应该运行哪个变体。

Here's my current implementation in a nutshell:简而言之,这是我当前的实现:

class MyBase
{
public:
    MyBase() { }
    virtual run() = 0;
}


class VariantA : public MyBase
{
public:
    VariantA () { }
    virtual run()
    {
        // Run code specific to hardware Variant-A
    }
}


class VariantB : public MyBase
{
public:
    VariantB () { }
    virtual run()
    {
        // Run code specific to hardware Variant-B
    }
}


void main()
{
    MyBase* variant;
    uint_8 switchValue = readSwitchValue();

    switch(switchValue)
    {
    case 0:
        variant = new VariantA();
        break;

    case 1:
        variant = new VariantB();
        break;
    }

    variant->run();
}

Now this works just fine.现在这工作得很好。 I read the hardware value and use a switch statement to create the new corresponding class.我读取硬件值并使用 switch 语句创建新的对应类。

The problem is that there are a lot of variants I have to deal with.问题是我必须处理很多变体。 Currently about 15, with the potential to add another 20-30 in the near future.目前大约有 15 个,在不久的将来有可能再增加 20-30 个。 I have really come to despise switch statements that run for hundreds of lines, so I'm really looking for a better way to do this, probably through templates.我真的开始鄙视运行数百行的 switch 语句,所以我真的在寻找一种更好的方法来做到这一点,可能是通过模板。

I want to be able to use my hardware value to look up a type and use that type to create my new object.我希望能够使用我的硬件值来查找类型并使用该类型来创建我的新对象。 Ideally when I add a new variant, I create the new class, add that class type to my lookup table with it's matching hardware value, and it's good to go.理想情况下,当我添加一个新变体时,我会创建新类,将该类类型添加到我的查找表中,并使用它匹配的硬件值,然后就可以了。

Is this possible at all?这可能吗? What's a good solution here?这里有什么好的解决方案?

As stated, you make a factory, but not necessarily with naive switch statements.如前所述,您创建了一个工厂,但不一定使用简单的 switch 语句。 What you can do is make a template class to create the relevant object and dynamically add these to your factory.您可以做的是创建一个模板类来创建相关对象并将它们动态添加到您的工厂中。

class VariantinatorBase {
  public:
    VariantinatorBase() {}
    virtual ~VariantinatorBase() {}
    virtual std::unique_ptr<Variant> Create() = 0;
};

template< class T >
class Variantinator : public VariantinatorBase {
  public:
    Variantinator() {}
    virtual ~Variantinator() {}
    virtual std::unique_ptr<Variant> Create() { return std::make_unique<T>(); }
};

Now you have a class factory that allows you to register these.现在你有一个类工厂,允许你注册这些。

class VariantFactory
{
  public:
    VariantFactory()
    {
         // If you want, you can do all your Register() calls in here, and even
         // make the Register() function private.
    }

    template< uint8_t type, typename T >
    void Register()
    {
        Register( type, std::make_unique<Variantinator<T>>() );
    }

    std::unique_ptr<Variant> Create( uint8_t type )
    {
        TSwitchToVariant::iterator it = m_switchToVariant.find( type );
        if( it == m_switchToVariant.end() ) return nullptr;
        return it->second->Create();
    }

  private:
    void Register( uint8_t type, std::unique_ptr<VariantinatorBase>&& creator )
    {
        m_switchToVariant[type] = std::move(creator);
    }

    typedef std::map<uint8_t, std::unique_ptr<VariantinatorBase> > TSwitchToVariant;
    TSwitchToVariant m_switchToVariant;
};

At the beginning of your program, create the factory and register your types:在你的程序开始时,创建工厂并注册你的类型:

VariantFactory factory;
factory.Register<0, VariantA>();
factory.Register<1, VariantB>();
factory.Register<2, VariantC>();

Then later, you want to call on it:然后,你想调用它:

std::unique_ptr<Variant> thing = factory.Create( switchValue );

You are looking for a factory您正在寻找工厂

http://www.oodesign.com/factory-pattern.html http://www.oodesign.com/factory-pattern.html

A factory is a software module (a method, a class) whose sole purpose is to create the right object for the job.工厂是一个软件模块(一个方法,一个类),其唯一目的是为工作创建正确的对象。 An example using a factory class:使用工厂类的示例:

class VariantFactory
{
    MyBase* CreateObject(uint_8 value);
}

And the CreateObject method can be filled out to give you the type of object that you need.并且可以填写 CreateObject 方法来为您提供所需的对象类型。

In the case of a very small selection of objects with simple construction, a simple switch statement might suffice.在选择非常少且构造简单的对象的情况下,一个简单的 switch 语句可能就足够了。 As soon as you get a lot of objects or ones that require more detailed construction, a factory is quite useful.一旦您获得大量对象或需要更详细构造的对象,工厂就非常有用。

I made this a comment;我做了这个评论; let's turn it into an answer:让我们把它变成一个答案:

Personally, I think a "switch/case" block to create the appropriate class is probably an optimal solution.就个人而言,我认为创建适当类的“switch/case”块可能是最佳解决方案。 Just put your case statement in a static "factory" method that returns a reference to the specific class.只需将您的 case 语句放在返回对特定类的引用的静态“工厂”方法中。 IMHO...恕我直言...

Here's a good example: factory method design pattern这是一个很好的例子: 工厂方法设计模式

Class Book : public Product
{
};

class Computer : public Product
{
};

class ProductFactory
{
public:
  virtual Product* Make(int type)
  {
    switch (type)
    {
      case 0:
        return new Book();
      case 1:
        return new Computer();
        [...]
    }
  }
}

Call it like this:

ProductFactory factory = ....;
Product* p1 = factory.Make(0); // p1 is a Book*
Product* p2 = factory.Make(1); // p2 is a Computer*
// remember to delete p1 and p2

Note that in his most excellent response, smink also suggests some other design alternatives, too.请注意,在他最出色的回应中,smink 还建议了其他一些设计替代方案。

BOTTOM LINE: There's nothing inherently "wrong" with a switch/case block.底线: switch/case 块本身并没有什么“错误”。 Even for a switch with many case options.即使对于具有多种外壳选项的开关。

IMHO...恕我直言...

PS: This really isn't creating a "dynamic type". PS:这真的不是在创建“动态类型”。 Rather, it's "creating a static type dynamically".相反,它是“动态创建静态类型”。 That would be equally true if you used a template or an enum solution as well.如果您也使用模板或枚举解决方案,那也同样如此。 But again - I vastly prefer the "switch/case".但同样 - 我非常喜欢“开关/案例”。

Update: I am leaving my original solution here for posterity, but consider the solution provided by paddy to be superior and less error prone.更新:我将我的原始解决方案留在这里供后代使用,但认为 paddy 提供的解决方案更出色且不易出错。 With only a couple of slight improvements I think it's actually about as good as you can possibly get.我认为只有一些细微的改进,它实际上与您可能获得的一样好。


Consider this design:考虑这个设计:

class VariantA : public MyBase
{
    static MyBase *CreateMachineInstance() { return new VariantA; }
};

class VariantB : public MyBase
{
    static MyBase *CreateMachineInstance() { return new VariantB; }
};

Now, all you need is an std::map that uses a uint_8 as the key and maps it to a function pointer (returning MyBase ).现在,您只需要一个std::map ,它使用uint_8作为键并将其映射到函数指针(返回MyBase )。 Insert the identifiers in the map (pointing each to the appropriate machine creation function) and then read the code and just use the map to find what machine you're using.在映射中插入标识符(将每个标识符指向适当的机器创建函数),然后阅读代码并仅使用映射来查找您正在使用的机器。

This is loosely based on a concept/pattern called a "factory" but may break slightly if your machine constructors require different arguments or you need to perform additional per-machine initialization/operations - and from what you mention it sounds like you might.这松散地基于称为“工厂”的概念/模式,但如果您的机器构造函数需要不同的参数,或者您需要执行额外的每台机器初始化/操作,则可能会稍微中断 - 从您提到的情况来看,这听起来像是可能的。

If that's the case, you can still use this pattern but you will have to make some tweaks and rearchitect things a bit but you will end up with something much cleaner and easier to augment and maintain.如果是这种情况,您仍然可以使用此模式,但您将不得不进行一些调整和重新构建一些东西,但最终会得到更清晰、更易于扩充和维护的东西。

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;

template<class T,class T1>
class HeroHonda
{
private:
    T millage;
    T1 *options;

public:
    HeroHonda() {
        puts("constructed");
        options=new T1[20];

        strcpy(options,"Good millage,Powerstart");
        millage=110;
    }

    virtual T features() {
        cout<<options<<"millage is"<<millage<<endl;
        return 1;
    }

    // virtual T Extrafeatures() = 0;

    ~HeroHonda() {
      cout<<"destructor"<<endl;
      delete [] options;    
    }
};

int main()
{
    HeroHonda <int,char> *Ptr=new HeroHonda <int,char>;
    Ptr->features();
    delete Ptr;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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