Official Codex Discord Server

  1. Welcome to rpgcodex.net, a site dedicated to discussing computer based role-playing games in a free and open fashion. We're less strict than other forums, but please refer to the rules.

    "This message is awaiting moderator approval": All new users must pass through our moderation queue before they will be able to post normally. Until your account has "passed" your posts will only be visible to yourself (and moderators) until they are approved. Give us a week to get around to approving / deleting / ignoring your mundane opinion on crap before hassling us about it. Once you have passed the moderation period (think of it as a test), you will be able to post normally, just like all the other retards.
    Dismiss Notice

How to get out of #include "stuff.h" hell?

Discussion in 'Codex Workshop' started by desocupado, Dec 2, 2015.

  1. desocupado Prophet

    desocupado
    Joined:
    Nov 17, 2008
    Messages:
    1,786
    It's actually surprising I didn't run into it until now.

    I have circular includes. -> means "includes"

    Map -> Grunt -> Entity -> Sludge_Cannon -> Map

    Sludge_Cannon -> Oil -> (is derived from) Map_Object -> Entity -> Sludge_Cannon

    Haha. :D

    Sludge_Cannon is what sparked the issue. It's derived from Module (which is abstract and will probably end an interface), which is like an accessory that can give passive boosts or a spell-like ability that may affect an Entity or the map. The problem is it needs to be able to modify the Map and/or Entities, but it's contained in a vector inside entity (and right now, initialized inside the constructor of one of Entity derived classes, although that's only temporary)

    Anyway, I tried forward declaration (which I might not have done properly), but it seems I can't do that if I end up using the pointers contained the classes.

    So... Any takers?
     
    Last edited: Dec 2, 2015
    ^ Top  
  2. DraQ Prestigious Gentleman Arcane

    DraQ
    Joined:
    Oct 24, 2007
    Messages:
    32,408
    Location:
    Chrząszczyżewoszyce, powiat Łękołody
    Wait, I'm not sure I understood you correctly, but why would map or entity need to be aware of your sludge_cannon anyway?
     
    ^ Top  
  3. Declinator Arbiter

    Declinator
    Joined:
    Apr 1, 2013
    Messages:
    542
    You have the forward declaration in the .h file like so:
    class Stuff;
    and then you #include stuff.h in the cpp file instead of the .h file which makes it possible to actually use the pointers.
     
    ^ Top  
  4. DraQ Prestigious Gentleman Arcane

    DraQ
    Joined:
    Oct 24, 2007
    Messages:
    32,408
    Location:
    Chrząszczyżewoszyce, powiat Łękołody
    It's actually the preferred way AFAIK.

    Avoids tons of unnecessary nested includes.
     
    ^ Top  
  5. desocupado Prophet

    desocupado
    Joined:
    Nov 17, 2008
    Messages:
    1,786
    Map needs to know Grunt, because Grunt is an enemy, and they're contained in the map file, which is like a level scenario. So when map generates the map based on the .txt file, it also generates the enemies on the proper positions.

    Grunt needs Entity because it derives from it.

    Entity needs Sludge_Cannon because Entity contains a vector with all modules, and Sludge_Cannon derives from Module, also on the constructor of Robot (derived from Entity), I call "new Sludge_Cannon".

    And Sludge_Cannon needs to know map, because the function "affects(Map* map)" uses map to put some sludge on some tiles.

    I also have the same problem more directly with

    Entity -> Weapon -> Entity

    Module -> Entity -> Module

    Entities needs to have weapons, and weapons affect entity.

    Modules modify entity which needs to contain modules.

    Right now, weapon creates a damage object which is then passed along to Entity, thus the circular including is not necessary, however, some weapons have some other effects other than damage, and having an Weapon::affect(Entity* ent) is a much more elegant solution than go around creating (possibly null objects, in case of no effect) to pass along.
     
    Last edited: Dec 2, 2015
    ^ Top  
  6. desocupado Prophet

    desocupado
    Joined:
    Nov 17, 2008
    Messages:
    1,786
    How about some more details on that (or a link) ? Which .h goes in which .cpp?
     
    Last edited: Dec 2, 2015
    ^ Top  
  7. Marquis de Lirium Cipher

    Marquis de Lirium
    Joined:
    Dec 1, 2013
    Messages:
    436
    • Brofist Brofist x 1
    ^ Top  
  8. desocupado Prophet

    desocupado
    Joined:
    Nov 17, 2008
    Messages:
    1,786
    I did try that, it's just that usually the results taught only the "you can't use the pointers" way. I'll make a more thorough search.
     
    ^ Top  
  9. Marquis de Lirium Cipher

    Marquis de Lirium
    Joined:
    Dec 1, 2013
    Messages:
    436
    Ok, I think can I see what your problem is: Forward declarations allow you to use pointers (or references) to a type, but will lead to errors in any expression that requires the size of the type to be known. Of course they also won't allow you to access members or methods of the forward declared type. In short, they are only useful in other declarations.

    Example: Your cannon class needs to know about maps. So, in cannon.h you write:

    For the implementation, though, the compiler needs the actual declaration. cannon.cpp will look somewhat like this:

    HTH.
     
    • Brofist Brofist x 1
    ^ Top  
  10. belowmecoldhands Savant

    belowmecoldhands
    Joined:
    Jan 4, 2014
    Messages:
    795
    I was making a GUI and encountered many instances of things like this OP, if it's any consolation to you. And I've programmed now and then for 20 years. I also used forward declerations and countless "tricks" unremembered.

    What's aggravating is I moved some code from MIcrosoft Visual C++ 2010 Express to Code::Blocks using Ubuntu-Kubuntu and the s*** wouldn't compile. And that's long after I trade to make it fit the c++ standard and not use ANY windows specific references. So I've left it on hte back burner since it's a headache. But let me tell you, this s*** will drive you mad.

    Most of it's just some simple googling. Still a pain in hte ass and hard to remember everyting. Good luck.

    EDIT: I found something online recently "YOU AIN"T GONNA NEED IT NOW". It's a catchphrase for "Don't program something unless you need it now." or "Don't bloat your projects with unnecessary things." It's also known as "YOU AIN'T GONNA NEED IT." But I find the first one more appropriate in my case, since "NOW" communicates better when I'm actually coding.

    I find myself thinking about it when I code. Maybe you'll think about it too.

    A description of it:
    http://www.extremeprogramming.org/rules/early.html

    More in-depth:
    http://www.infoq.com/news/2015/06/yagni

    A video explanation (watch the whole thing!):
     
    Last edited: Dec 5, 2015
    ^ Top  
  11. gaussgunner Arcane

    gaussgunner
    Joined:
    Jul 22, 2015
    Messages:
    3,797
    Location:
    Worst Kwa
    You can prevent circular/multiple includes put putting #pragma once at the top of each .h file, or the old way, by wrapping it in #define FILENAME_H #ifndef FILENAME_H .... #endif.

    But it sounds like you're gonna end up mapping out your data structures on paper and rebuilding half of your engine. I've been there, lol.

    It's not you. C++ is shit and the way they teach it (*and Java) in universities is retarded.
    I've learned some workarounds:
    - Avoid inheritance, templates, exceptions, operator overloading, constructors and destructors (mostly...)
    - Avoid STL strings streams maps vectors lists etc when you care about performance
    - Use fixed size arrays whenever you can put an upper bound on the number of items
    - Use compositional OO (pointer to "superclass" object instead of inheriting it)
    - Define init(...) methods to use instead of constructors
    - Don't even use OO when you don't have to
     
    ^ Top  
  12. Viata Arcane

    Viata
    Joined:
    Nov 11, 2014
    Messages:
    6,344
    Location:
    Water Play Catarinense
    So the best workaround of C++ is to not use what C++ was made for: OOP?
     
    • Brofist Brofist x 1
    ^ Top  
  13. 28.8bps Modem Prophet

    28.8bps Modem
    Joined:
    Jan 15, 2014
    Messages:
    302
    Location:
    The Internet, Circa 1993
    Forward declarations are the right way to go. Since it hasn't been said before, use forward declarations in preference to inclusion in headers where ever the compiler doesn't the need the full definition to calculate the size of the thing.

    So, if you have:

    Code:
    class Foo
        {
    private:
        Bar* mBar;
         };
    
    Since you don't need the full definition of the class Bar to calculate the size of Foo, just forward declare it in the header and include the actual definition only in the implementation. I tend to use this as a hard and fast rule, even when the forward declared thing is publicly accessible, so the user of the class would have to include both foo.h and bar.h to use it, but it's debatable as to whether this is good style or not.

    Also...

    This advice hurts my soul. Do not listen.
     
    ^ Top  
  14. gaussgunner Arcane

    gaussgunner
    Joined:
    Jul 22, 2015
    Messages:
    3,797
    Location:
    Worst Kwa
    Yeah. Won't solve all his probs but still good advice.

    Not quite... just ignore the dogma. OO is basically glorified C structs. It's about keeping your program state manageable. All that other shit is bells and whistles and failed experiments. Some of it works in other langs but it falls apart when you bolt it onto C.

    C++ is pretty bad for gamedev. Check out Mike Acton's "Data Oriented Design" talk, and Jon Blow's videos about his new language if you can stand 'em.
     
    • Brofist Brofist x 1
    ^ Top  
  15. Ranselknulf Arcane Patron

    Ranselknulf
    Joined:
    Nov 28, 2012
    Messages:
    1,873,624
    Location:
    Best America
    PC RPG Website of the Year, 2015 Codex 2016 - The Age of Grimoire Make the Codex Great Again! Grab the Codex by the pussy
    This thread isn't very :incloosive:
     
    • Brofist Brofist x 2
    ^ Top  
  16. J1M Arcane

    J1M
    Joined:
    May 14, 2008
    Messages:
    11,315
    Exactly.
     
    • Brofist Brofist x 1
    ^ Top  
  17. flabbyjack Arcane

    flabbyjack
    Joined:
    Jul 15, 2004
    Messages:
    2,361
    Location:
    the area around my keyboard
    That kind of OOP crap just gets in the way. Modern compilers should resolve circular references, anyway.
     
    ^ Top  
  18. DraQ Prestigious Gentleman Arcane

    DraQ
    Joined:
    Oct 24, 2007
    Messages:
    32,408
    Location:
    Chrząszczyżewoszyce, powiat Łękołody
    desocupado
    Could you include (heh) a dependency diagram or stripped down class definitions (with prototypes of relevant methods)?
    I'm not quite getting WHY you're ending with a circular reference in the first place.


    Edit:

    NVM, you seem to have answered that question already, will respond tomorrow.
     
    Last edited: Dec 13, 2015
    ^ Top  
  19. DraQ Prestigious Gentleman Arcane

    DraQ
    Joined:
    Oct 24, 2007
    Messages:
    32,408
    Location:
    Chrząszczyżewoszyce, powiat Łękołody
    The problem is not that you're using OOP but precisely that you don't use it (also, disregard ignorant rabble).
    You need to make use of some polymorphism.
    Map doesn't need to know any specific classes for enemies or whatever. Neither does Entity.
    What you do need is a base class with defined interface (and virtual methods including destructor) that is known by your Entity, then having derived classes for specific things, each complete with factory class (also inheriting from base abstract factory) that can construct it and return the pointer to base. You can hold a single instance of factory as static member of derived and even have it register itself on construction into some sort of global container object (make it singleton to avoid static initialization fiasco) to make it known to map or whatever you need to at runtime but not at compile time, meaning you can now add new classes without having to modify code using them (like map).


    That's actually THE way to go.
    You don't want a weapon to be messing around with internals of another object (no matter how true to life it might be) it's affected class' responsibility to know to how it should react to being hit by particular attack so it's best to pass a damage object to it and let it react in appropriate manner. This way each and every of your weapons doesn't have to be aware of each and every of your potential targets.

    You can decouple a lot of things in similar manners, for example by having both Map and your Sludge_Cannon depend on Tile, or having your Sludge_Cannon pass along some sort of tile effect object (that tile could react to appropriately, even if it is the kind of tile your Sludge_Cannon doesn't know about).

    It's also worth considering whether you want things that are affected by weapons and things that can wield weapons at the exact same point in the class hierarchy - maybe it would be better for all kinds of generic object to allow passing damage to them (even including other weapons) and only combatants to actually use weapons?

    Multiple inheritance can be used to give particular classes more interfaces should you need it.
     
    • Brofist Brofist x 2
    ^ Top  
  20. Castanova Prophet

    Castanova
    Joined:
    Jan 11, 2006
    Messages:
    2,949
    Location:
    The White Visitation
    In my experience, you're going down a very deep and disturbing rabbit hole with this sort of code design. Except in very simple games, it's not a good idea to have the actual "entities" (such as Sludge Cannon) responsible for behavior or processing.

    What if you want Sludge Cannon to behave differently based on the skills of who is using it? What if it behaves differently depending on what turn the combat is on? If it depends on how far into the game you are? If it depends on the type of target it is shooting? You can very quickly create an interlocking web where everything depends on everything else and any simple change to your game design creates a cascading effect of breakages and forced code rewrites.

    Honestly if you're not that far into it, I would scrap your current design and start over... I can't tell you how many projects I've started and abandoned for this reason. The current project I'm working on I'm WAY further into it then I've ever gotten and I'm using a flatter more data-oriented code design.
     
    ^ Top  
  21. desocupado Prophet

    desocupado
    Joined:
    Nov 17, 2008
    Messages:
    1,786
    I see the issue, and in fact, I've ran into this issue with buffs (weapons are more accurate if the user has a +acc buff for example), but I don't quite know how else to proceed. So far everything is working ok (few hacks here and there) and I hope they'll stay this way, but how would you manage this? Let's say you do have a piece of equipment that has all these things. Depends on the turn, how far into the game, the target and the terrain. How would you implement that?
     
    ^ Top  
  22. Castanova Prophet

    Castanova
    Joined:
    Jan 11, 2006
    Messages:
    2,949
    Location:
    The White Visitation
    Here's what I've been doing...

    You may have a data structure for weapons or items in general:

    struct Item
    {
    int itemType;
    int subType;
    int damage;
    ... // other data
    };

    In this case, itemType = ITEM_WEAPON and subType = ITEM_SLUDGE_CANNON.

    Then, instead of Item having a method called "Shoot()" or something like that, you can have "System" classes or functions which handle all the actual logic. Like, you may implement a class like this:

    class CombatRuleset
    {
    void shoot(Unit *shooter, unit *target, item *weapon);
    };

    The ruleset generally has no state. It just operates on inputs that you give it. So, only CombatRuleset has to deal with dependencies and you'll never have circular references or anything because nothing else really depends on CombatRuleset.

    To answer my random example, it could be like:

    void shoot(Unit *shooter, unit *target, item *weapon, int gameTime, int turnCount, int terrainType);

    The other benefit of this is you can then store all your game data in external data files or maybe a database and you can create a "Generator" class that reads the data from the database and instantiates, say, a Sludge Cannon. Then you can change game balance without having to recompile the code. Also, since all your game logic is contained in these "System" classes, you can even take that out of your C++ or whatever you're using and put it into, say, Lua... then you can even change your game logic without recompiling. And your game would be moddable.
     
    • Brofist Brofist x 3
    ^ Top  
  23. Leshy Educated

    Leshy
    Joined:
    Mar 9, 2014
    Messages:
    33
    Location:
    Cydonia, Mars
    What? If that is your way of programming stick to C, Fortran or COBOL or whatever other ancient technology you like. But if you use C++, please do use OO and all what comes with it, just don't go full retard (virtual inheritance - just say no).

    And for OP. Use #pragma once or just switch to Java or (yuck...) C#.
     
    ^ Top  
  24. Marquis de Lirium Cipher

    Marquis de Lirium
    Joined:
    Dec 1, 2013
    Messages:
    436
    #pragma once is non-standard and has subtly different behavior than include guards. That's exactly the kind of stuff that's bound to blow up in your face when you expect it the least.

    Oh, and some of gaussgunners tips are in fact quite reasonable. The C++ FQA is mandatory reading for everyone stuck with this god-awful language.
     
    • Brofist Brofist x 1
    ^ Top  
  25. DraQ Prestigious Gentleman Arcane

    DraQ
    Joined:
    Oct 24, 2007
    Messages:
    32,408
    Location:
    Chrząszczyżewoszyce, powiat Łękołody
    :bro:


    Personally I consider C++ awesome and fun* :M , but I'd still recommend FQA (*after* FAQ it references) for a distanced, and somewhat amusing second look. It's not good getting saddled with tunnel vision, no matter the exact direction you're looking in.

    * (open)
    Of course, debugging and maintaining badly written C++ code *will* drive you certifiably insane and it's sadly remarkably easy to make awful, deranged shit in C++.
     
    Last edited: Mar 16, 2016
    • Brofist Brofist x 1
    ^ Top  

As an Amazon Associate, rpgcodex.net earns from qualifying purchases.