Long list of struct members

Viewed 159

I got a struct with lots of members. I decided to write some functions for my struct but it just feels wrong typing all members as parameter into the function call. Is there any way to shorten this?

items::items(std::string name_, std::string role_, uint16_t prize_, uint16_t phys_power_, uint16_t mag_power_,
             uint16_t attack_speed_, uint16_t life_steal_, uint16_t flat_pen_, uint16_t perc_pen_, uint16_t crit_,
             uint16_t phys_prot_, uint16_t mag_prot_, uint16_t cc_red_, uint16_t health_, uint16_t hp5_, uint16_t speed_,
             uint16_t cd_red_, uint16_t mana_, uint16_t mp5_) {
    name = name_;
    role = role_;
    prize = prize_;
    phys_power = phys_power_;
    mag_power = mag_power_;
    attack_speed = attack_speed_;
    life_steal = life_steal_;
    flat_pen = flat_pen_;
    perc_pen = perc_pen_;
    crit = crit_;
    phys_prot = phys_prot_;
    mag_prot = mag_prot_;
    cc_red = cc_red_;
    health = health_;
    hp5 = hp5_;
    speed = speed_;
    mana = mana_;
    mp5 = mp5_;
}

void items::print_item(items s) {
    std::cout << name << " " << role  << " " << prize << " " << phys_power << " " << mag_power << " " << attack_speed << " " << life_steal << " " << flat_pen <<
    " " << perc_pen << " " << crit << " " << phys_prot << " " << mag_prot << " " << cc_red << " " << health << " " << hp5 << " " << speed << " " << mana << " " <<
    mp5 << std::endl;    
}
2 Answers

We can solve your problem using the builder pattern. We will have a very simple constructor for item, that only takes the name as param. We will build the rest of the attributes using a builder pattern. Let's start with a forward declaration of the ItemBuilder and the Item struct.

struct ItemBuilder;

struct Item
{
  Item (std::string name_):name (name_)
  {
  };
  static ItemBuilder getBuilder(std::string name_);
  std::string name;
  std::string role;
  uint16_t prize;
  uint16_t phys_power;
  uint16_t mag_power;
  uint16_t attack_speed;
  uint16_t life_steal;
  uint16_t flat_pen;
  uint16_t perc_pen;
  uint16_t crit;
  uint16_t phys_prot;
  uint16_t mag_prot;
  uint16_t cc_red;
  uint16_t health;
  uint16_t hp5;
  uint16_t speed;
  uint16_t cd_red;
  uint16_t mana;
  uint16_t mp5;
};

Now let's write our ItemBuilder.

struct ItemBuilder
{
private:
  Item m_item;
public:

  ItemBuilder (std::string name_):m_item (name_)
  {
  }

  operator  Item () const
  {
    return std::move (m_item);
  };

  ItemBuilder & role (std::string role_)
  {
    m_item.role;
    return *this;
  };
  ItemBuilder & prize (uint16_t prize_)
  {
    m_item.prize = prize_;
    return *this;

  };
  ItemBuilder & phys_pow (uint16_t phys_power_)
  {
    m_item.phys_power = phys_power_;
    return *this;

  };
  ItemBuilder & mag_power (uint16_t mag_power_)
  {
    m_item.mag_power = mag_power_;
    return *this;

  };
  ItemBuilder & attack_speed (uint16_t attack_speed_)
  {
    m_item.attack_speed = attack_speed_;
    return *this;

  };
  ItemBuilder & life_steal (uint16_t life_steal_)
  {
    m_item.life_steal = life_steal_;
    return *this;
  };
  ItemBuilder & flat_pen (uint16_t flat_pen_)
  {
    m_item.flat_pen = flat_pen_;
    return *this;
  };
};

Finally, we should not forget to Item::getBuilder

ItemBuilder Item::getBuilder(std::string name_) { return ItemBuilder (name_); }

int
main ()
{
  auto builder = Item::getBuilder("any");
  Item it = builder.role("mage").phys_pow(1).attack_speed(2).mag_power(100);
  std::cout << it.name << " " << it.phys_power << " " << it.attack_speed << " " << it.mag_power;
  return 0;
}

Run the code


Useful links:

I assume these are characters or, well, items from some kind of game. I assume there is a finite number of item types (mage, warrior, dwarf etc.), or possibly a number of "families" of items, each with a few "sub-types".

Edit: The first step is actually to have reasonable default values for members so that you only need to set those that differ. In C++11 and later you can simply assign a default in the member declaration, seehttps://en.cppreference.com/w/cpp/language/data_members, which can be combined with a proper constructor.

A possibility for prepared sets of values is to define a constant prototype for each "type" with standard settings. Only a few of the members then need to be modified in each instance after initialization with the proper prototype.

Another possibility is to divide your large structure into sub-structures which can be set separately: E.g. magical abilities, physical abilities, current condition etc. These could be combined to create basic items which then can be customized by setting single properties as fit.

The division into smaller sub-units could also be accomplished by an inheritance hierarchy; but complex inheritance has fallen out of favor because it couples code too tightly. Instead, composition appears to lead to code that's better understandable and maintainable (and isn't that almost the same?). But at least a couple of interfaces may be in order which allow for the uniform treatment of items where appropriate.

Edit: Since you mention it in the comments: Constructing items from a text file is a great idea. It lets you create several different item instances without recompiling. I would recommend a very simple xml structure that you either parse manually or with a library (TinyXML2?). Alternatively you can also start with an even simpler comma separated list (but probably still key/value pairs so that you can omit some and leave them at default values!) which you can also create, read or edit in Excel (or LibreOffice etc.).

Related