简体   繁体   中英

How to iterate through all typenames in a class template?

I want to design a component-based weapon template for my game. However, it seems no way to add/remove a class member or create a code?

Sorry for my expression and lack of terminology, for I am not graduated from dept. of computer science or software engineer, I know little of what those stuff called by professionals.

Here is the component code looks like:

class CBaseWpnCmpt : public std::enable_shared_from_this<CBaseWpnCmpt>
{
public:
    typedef std::shared_ptr<CBaseWpnCmpt> PTR;

private:
    CBaseWpnCmpt() = default;

public:
    CBaseWpnCmpt(const CBaseWpnCmpt& s) = default;
    CBaseWpnCmpt(CBaseWpnCmpt&& s) = default;
    CBaseWpnCmpt& operator=(const CBaseWpnCmpt& s) = default;
    CBaseWpnCmpt& operator=(CBaseWpnCmpt&& s) = default;
    virtual ~CBaseWpnCmpt() {}

protected:
    CBaseWeaponInterface::PTR m_pWeapon { nullptr };

public:
    template <class CComponent>
    static std::shared_ptr<CComponent> Create(CBaseWeaponInterface::PTR pWeapon)
    {
        std::shared_ptr<CComponent> pComponent = std::make_shared<CComponent>();
        pComponent->m_pWeapon = pWeapon;
        return pComponent;
    }
};

And this is what a weapon body code looks like: (And the problem occurs)

template <  class CWeapon,
            class ...CComponents
>
class CBaseWeaponTemplate : public CBaseWeaponInterface
{
public:
    std::list<CBaseWpnCmpt::PTR>    m_lstComponents;

public:
    virtual void    SecondaryAttack(void)   // Example method.
    {
        for (auto& pComponent : m_rgpComponents)
        {
            pComponent->SecondaryAttack();
        }
    }
};

How am I suppose to create all these argument packs as member of the template? Currently I tried to enlist them into a pointer std::list container, but I just can't figure out how to achieve it at all.

In other words, how can I make a template when I fill in blank likt this:

class CAK47 : public CBaseWeaponTemplate<CAK47, CLongMagazine, CWoodenStock>

will generate this:

class CAK47
{
    CLongMagazine m_comp1;
    CWoodenStock m_comp2;
//... other stuff
};

Or alternatively, generate this:

class CAK47
{
    CAK47() // constructor
    {
        for (/* somehow iterate through all typenames */)
        {
            CBaseWpnCmpt::PTR p = std::make_shared<typename>();
            m_lstComponents.emplace_back(p);
        }
    }
};

One way of doing so from C++11 on-wards would be to store the template types used for this particular weapon inside an std::tuple

template <typename Weapon, typename... Attachments>
class WeaponWithAttachments {
  protected:
    WeaponWithAttachments() {
    return;
    }
    std::tuple<Attachments...> attachment_types;
};

and then using that tuple to initialise a vector of shared pointers with a protected constructor taking a tuple to access the template types again.

class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
  public:
    SomeWeaponWithAttachments()
      : SomeWeaponWithAttachments{attachment_types} {
      return;
    }
  protected:
    template <typename... Attachments>
    SomeWeaponWithAttachments(std::tuple<Attachments...> const&)
      : attachments{std::make_shared<Attachments>()...} {
      return;
    }
    std::vector<std::shared_ptr<BaseAttachment>> attachments;
};

Try it here!


If the attachments vector is already declared inside the parent class like it seems to be the case for you might also avoid the tuple and the protected constructor with initialising the attachments already inside the parent class

template <typename Weapon, typename... Attachments>
class WeaponWithAttachments {
  protected:
    WeaponWithAttachments()
      : attachments{std::make_shared<Attachments>()...} {
      return;
    }
    std::vector<std::shared_ptr<BaseAttachment>> attachments;
};

and then only calling the constructor of the base class in the derived class

class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
  public:
    SomeWeaponWithAttachments()
      : WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment>() {
      return;
    }
};

Try it here!


If that is no option for you, then you can use the tuple to iterate over all the template arguments using C++17 fold expressions:

class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
  public:
    SomeWeaponWithAttachments()
      : SomeWeaponWithAttachments{attachment_types} {
      return;
    }
  protected:
    template <typename... Attachments>
    SomeWeaponWithAttachments(std::tuple<Attachments...> const&) {
      (attachments.push_back(std::make_shared<Attachments>()), ...);
      return;
    }
};

Try it here!


In C++17 you might also add a static assertion with fold expressions into the constructor to make sure that the types actually inherit from BaseAttachment :

static_assert((std::is_base_of_v<BaseAttachment, Attachments> && ...), "Template arguments must inherit from 'BaseAttachment'.");

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