9
\$\begingroup\$

There are two classes in my game that are really useful, but slowly becoming a pain. Message and Property (property is essentially a component).

They both derive from a base class and contain a static id so that systems can only pay attention to the ones they want to. It's working out very well... except...

I'm constantly making new message types and property types as I extend my game. Each time I need to write 2 files (hpp and cpp) and a ton of boilerplate to get essentially an classID and one or two standard data types or a pointer.

It's starting to make playing around and testing out new ideas a real chore. I wish when I want to create a new message or property type, I want to be able to just type something like

ShootableProperty:  int gunType, float shotspeed;

ItemCollectedMessage:  int itemType;

instead of creating a header and cpp file, writing a constructor, including the parent class, etc etc.

It's around 20 - 40 lines (including include guards, and everything) just to do what logically is 1 or 2 lines in my mind.

Is there some programming pattern to get around this?

What about with scripting (which I know nothing of)... is there a way to define a bunch of classes that are almost the same?


Here is exactly what one class looks like:

// Velocity.h

#ifndef VELOCITY_H_
#define VELOCITY_H_

#include "Properties.h"

#include <SFML/System/Vector2.hpp>

namespace LaB
{
namespace P
{

class Velocity: public LaB::Property
{
public:
    static const PropertyID id;

    Velocity(float vx = 0.0, float vy = 0.0)
    : velocity(vx,vy) {}
    Velocity(sf::Vector2f v) : velocity(v) {};

    sf::Vector2f velocity;
};

} /* namespace P */
} /* namespace LaB */
#endif /* LaB::P_VELOCITY_H_ */



// Velocity.cpp

#include "Properties/Velocity.h"

namespace LaB
{
namespace P
{

const PropertyID Velocity::id = Property::registerID();

} /* namespace P */
} /* namespace LaB */

All of that just for a 2D Vector and an ID saying to expect a 2D vector. (Granted, some properties have more complicated elements, but it's the same idea)

\$\endgroup\$
  • 3
    \$\begingroup\$ Take a look at this, it might help you. gameprogrammingpatterns.com/type-object.html \$\endgroup\$ – user35339 Sep 21 '13 at 14:41
  • 1
    \$\begingroup\$ All this looks like templates might help, just not with the static initialization. It can certainly be made a lot easier with in this case, but it's hard to tell without more information on what you want to do with those IDs and why you are using inheritance at all. Using public inheritance but no virtual functions is definitely a code smell... This is not polymorphism! \$\endgroup\$ – ltjax Sep 23 '13 at 15:20
  • \$\begingroup\$ Have you found the 3rd party library that implements it? \$\endgroup\$ – Boris Sep 24 '17 at 15:22
1
\$\begingroup\$

C++ is powerful, but it's verbose. If what you want is lots of small polymorphic classes that are all different, then yes, it's going to take lots of source code to declare and define. There's nothing to do about it, really.

Now, as ltjax said, what you're doing here isn't exactly polymorphism, at least for the code you've provided. I can't see a common interface that would hide the specific implementation of sub-classes. Except maybe for the class ID, but this is actually redundant with the real class ID: it's name. This seems to be just a bunch of classes containing some data, nothing really complicated.

But this doesn't solve your problem: you want to create lots of messages and properties with the least amount of code. Less code means less bugs so this is a wise decision. Unfortunately for you there's no single solution. It's hard to tell what would suit your needs the most without knowing precisely what you intend to do with those messages and properties. So let me just expose your options:

  1. Use C++ templates. Templates are a great way to let the compiler "write code" for you. It's becoming more and prominent in the C++ world, as the language evolves to support them better (e.g. you can now use a variable number of template parameters). There's a whole discipline dedicated to the automated generation of code through templates: template metaprogramming. Problem is: it's tough. Don't expect non-programmers to be able to add new properties themselves if you plan to use such a technique.

  2. Use plain-old C macros. It's old school, easy to over-abuse, and error-prone. They are also very simple to create. Macros are just glorified copy-pastes performed by the preprocessor, so they're actually quite suited to create tons of things that are almost the same with little variations. But don't expect other programmers to love you for using them, as macros are often used to hide flaws in the overall program design. Sometimes, though, they're helpful.

  3. Another option is to use an external tool to generate the code for you. It's already been mentioned in a previous answer so I won't expand on that.

  4. There are other languages that are less verbose and will allow you to create classes way more easily. So if you already have script bindings (or you plan on having them), defining those classes in script might be an option. Accessing them from C++ will be quite complicated though, and you'll lose most of the benefit of the simplified creation of classes. So this is probably viable only is you plan on doing most of your game logic in another language.

  5. Finally last but not least, you should consider using a data-driven design. You could be define those "classes" in simple text files using a layout similar to the one you proposed yourself. You'll have to create a custom format and parser for it, or use one of the several already available options (.ini, XML, JSON and whatnot). Then on the C++ side, you'll need to create a system that supports a collection of those different kinds of objects. This is almost equivalent to the scripting approach (and probably requires even more work), except you'll be able to tailor it more precisely to your needs. And if you make it simple enough, non-programmers might be able to create new things by themselves.

\$\endgroup\$
0
\$\begingroup\$

How about a code generation tool to help you out?

for example, you could define your message type and members in a small text file and have the code gen tool parse it and write all the boilerplate C++ files as a pre-build step.

There are existing solutions like ANTLR or LEX/YACC, and it would not be hard to roll your own depending on complexity of your properties and messages.

\$\endgroup\$
  • \$\begingroup\$ Just an alternative to LEX/YACC aproach, when I have to generate very similar files with just minor modifications, I use a simple and small python script and a C++ code template file containing tags. The python script searches for these tags in the template replacing them by the representative tokens of the element being created. \$\endgroup\$ – victor Sep 25 '13 at 15:38
0
\$\begingroup\$

I advise you to research about Google's Protocol Buffers (http://code.google.com/p/protobuf/). They are a very clever way to handle generic messages. You just have to specify the properties in a struct-llike pattern and a code generator do all the job of generating the classes for you (Java, C++ or C#). All generated classes have text and binary parsers, which make them good for both text-based message initialization and serialization.

\$\endgroup\$
  • \$\begingroup\$ My messaging central is core my program -- do you know if protocol buffers incurs any large overhead? \$\endgroup\$ – Bryan Sep 26 '13 at 12:11
  • \$\begingroup\$ I don't think they incur large overhead, since the API is just a Builder class for each message and the class for message itself. Google uses them for the core of their infrastructure. For example Google App Engine Entities are all converted to protocol buffers before persisted. If you are in doubt, I advise you to implement a comparison test between your current implementation and protocol buffers. If you think the overhead is acceptable, use them. \$\endgroup\$ – dsilva.vinicius Sep 26 '13 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.