Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 ... 541 542 [543] 544 545 ... 796

Author Topic: if self.isCoder(): post() #Programming Thread  (Read 907281 times)

Reelya

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8130 on: October 08, 2015, 03:32:12 pm »

There are a couple of ways to do it.

One way is to make a virtual function that retrieves a pointer to the static data. Then, each subclass has their own accessor for their own data. The actual data wouldn't need to be stored in any specific place, it could be in a separate array of objects.

But there's also a way that uses templates that I used one time, that can replicate the static data correctly for each subclass without needing virtual functions (which are more overhead). It has to do with the fact that template classes are replicated for all instances with different parameters. I liked this solution since it worked out pretty elegant. Each object inherits from the template, and the template also takes the current class as a parameter. Then you're forced to make a separate static object for each subclass, so there's pretty strict checking that everything exists like it should.

The flyweight solutions involve each individual object having a pointer to it's personal data, which could be more overhead than necessary, if you'd like specific classes to have shared data. But it is more flexible (you're softcoding the class heirarchy instead of hard coding it)
« Last Edit: October 08, 2015, 03:35:54 pm by Reelya »
Logged

MagmaMcFry

  • Bay Watcher
  • [EXISTS]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8131 on: October 08, 2015, 04:14:51 pm »

Hmmm. Can't you just add static members to your classes, then retrieve the static data via overridable accessor methods?
Logged

TheBiggerFish

  • Bay Watcher
  • Somewhere around here.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8132 on: October 08, 2015, 05:23:45 pm »

Well, I tried installing Arch. It all went well until I missed a step, costing me my C drive's data. Thankfully most of my data was in my D drive. Unfortunately, not all of it. Last system image? January.

So yeah. I should not be allowed to touch computers.
Ctrl-z?
Things To Not Ever Let Me Do:  Muck About With SRS SOFTWARE STUFF.
Logged
Sigtext

It has been determined that Trump is an average unladen swallow travelling northbound at his maximum sustainable speed of -3 Obama-cubits per second in the middle of a class 3 hurricane.

Gatleos

  • Bay Watcher
  • Mournhold... City of Light... City of MAGIC!
    • View Profile
    • Someone Sig This
Re: if self.isCoder(): post() #Programming Thread
« Reply #8133 on: October 08, 2015, 06:53:45 pm »

Hmmm. Can't you just add static members to your classes, then retrieve the static data via overridable accessor methods?
It's not static in the OOP sense, what I mean is it's data that doesn't change over time and is pointed to by every object of that type. The reason it works this way is because I've moved the definitions of this static data out to JSON files. A concrete example would probably help.

Code: [Select]
"si_town": {
"name": "Town",
"full": {
"animFile": "castle.anim",
"idle": "town"
},
"half": {
"animFile": "monsters.anim",
"idle": "Bowser"
},
"quarter": {
"animFile": "monsters.anim",
"idle": "Bowser"
}
}

The data files look like this (this one is describing the animation files to load when displaying a town). As you can imagine, all towns in the program have a lot of data in common. So, they point to the single instance of town data. There are others for other types of sites.

There are a couple of ways to do it.

One way is to make a virtual function that retrieves a pointer to the static data. Then, each subclass has their own accessor for their own data. The actual data wouldn't need to be stored in any specific place, it could be in a separate array of objects.

But there's also a way that uses templates that I used one time, that can replicate the static data correctly for each subclass without needing virtual functions (which are more overhead). It has to do with the fact that template classes are replicated for all instances with different parameters. I liked this solution since it worked out pretty elegant. Each object inherits from the template, and the template also takes the current class as a parameter. Then you're forced to make a separate static object for each subclass, so there's pretty strict checking that everything exists like it should.

The flyweight solutions involve each individual object having a pointer to it's personal data, which could be more overhead than necessary, if you'd like specific classes to have shared data. But it is more flexible (you're softcoding the class heirarchy instead of hard coding it)

That's an interesting way to handle it, can you go into more detail? I'm guessing passing the class as a template parameter is just for telling the compiler to write getStaticData functions for each inheriting class. I'm trying to wrap my head around the implications of inheriting from a template class that also takes the class as a parameter.
Logged
Think of it like Sim City, except with rival mayors that seek to destroy your citizens by arming legions of homeless people and sending them to attack you.
Quote from: Moonshadow101
it would be funny to see babies spontaneously combust
Gat HQ (Sigtext)
++U+U++ // ,.,.@UUUUUUUU

MagmaMcFry

  • Bay Watcher
  • [EXISTS]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8134 on: October 08, 2015, 07:05:52 pm »

Oh, I see. In this case I would suggest you actually just make one big class per type of data (in your example case a Creature class and a CreatureType class). Don't do inheritance, it really really isn't worth the effort. Also I suggest keeping all your objects indexed by name in a dictionary (in this case a Map<String, Creature> and a Map<String, CreatureType>), and instead of storing a reference to another object, store its name (in this example, a Creature would store the name of its CreatureType, not a reference or pointer). This is great for stability and flexibility, the performance loss is negligible (say no to premature optimization, and should you ever be in dire need of performance, you can always change that back, although you're most likely doing something else wrong).

I'm speaking from experience here, I've implemented the thing you're trying to do many times now.
Logged

Reelya

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8135 on: October 09, 2015, 12:45:15 am »

Hmmm. Can't you just add static members to your classes, then retrieve the static data via overridable accessor methods?

The issue with that is that every single object must then carry around the virtual function pointers for the correct methods. If four virtual functions are needed to get and set two pieces of static data, then that's four 32 bit pointers you've added to each and every object in your simulation. And it's really inflexible and error-prone. If someone extends your heirarchy but forgets to add the static member or virtual functions, then the system breaks without any error messages or warnings.

Here's an example. Say you decide to use inheritance for a system where there are Persons who can be Teacher or Student. And there are two methods which must be implemented by polymorphism. You might think to make a Person base class, make child classes of Teacher and Student. But now, to put them in an array, you must make a vector<Person*> - an array of person pointers, and then create dynamic Teacher and Student objects, accessible through the array of Person pointers. So that's three additional pointers per Person (one for the polymorphic pointer and two for the virtual functions), to do the job. Here's the code:
Spoiler (click to show/hide)

Ok, we can do a lot better than that but keep all the same powers.

How about we take the polymorphism and encapsulate that inside our base class? The idea is that each Person object is a container which holds a pointer to a "PersonType" object, and it's these PersonType objects which keep track of all the types. Now, since you only have one type of object (Person) in your data structure, you no longer need to address the persons via pointers, you can just pack the person data type right into memory. And the virtual function pointers are now replicated once per PersonType objects rather than Person objects. Think of the savings! Additionally, by "softcoding" the types, you can change an object from one type to another at runtime without needing to make a new object. Here's the code:
Spoiler (click to show/hide)
« Last Edit: October 09, 2015, 02:17:02 am by Reelya »
Logged

i2amroy

  • Bay Watcher
  • Cats, ruling the world one dwarf at a time
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8136 on: October 09, 2015, 11:05:07 am »

Well, I tried installing Arch. It all went well until I missed a step, costing me my C drive's data. Thankfully most of my data was in my D drive. Unfortunately, not all of it. Last system image? January.

So yeah. I should not be allowed to touch computers.
There is a reason why they always say "make sure to back up your computer before upgrading/fiddling with the OS". :P

Seriously, I've been saved like five times now by my backups due to upgrades/installations/updates going horribly wrong.
Logged
Quote from: PTTG
It would be brutally difficult and probably won't work. In other words, it's absolutely dwarven!
Cataclysm: Dark Days Ahead - A fun zombie survival rougelike that I'm dev-ing for.

Reelya

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8137 on: October 09, 2015, 11:30:18 am »


That's an interesting way to handle it, can you go into more detail? I'm guessing passing the class as a template parameter is just for telling the compiler to write getStaticData functions for each inheriting class. I'm trying to wrap my head around the implications of inheriting from a template class that also takes the class as a parameter.

Well two relevant models are the Flyweight Pattern:
http://gameprogrammingpatterns.com/flyweight.html

And i looked up the idea I mentioned, it's under this:
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

This is the relevant code snippet:
Code: [Select]
// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
    // methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
    // ...
};

And this is it extended to do the static data thing:


Code: [Select]
#include <iostream>

// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
public:
    static int data;
    // methods within Base can use template to access members of Derived
    int &GetStatic()
    {
        return T::data;
    }
};
class Derived1 : public Base<Derived1>
{
    // ...
};

int Base<Derived1>::data = 1;

class Derived2 : public Base<Derived2>
{
    // ...
};

int Base<Derived2>::data = 2;

int main()
{
std::cout << Derived1::data << std::endl;
std::cout << Derived2::data << std::endl;
getchar();
return 0;
}

So this does away with all virtual functions, and each derived class gets a separate static member you need to implement, and you'll get a compiler error if you forget to instantiate it. Note, this is very efficient with memory etc, but has flexibility problems if you want deeply nested heirarchies. The flyweight pattern is better for those. Note that if you want to use these in a polymorphic array, you need to make a class above "Base" e.g. "BaseBase" which is not a template. Template classes are not the same class if their template parameter differs, so a pointer to "Base" cannot be created and used.

There's more on the above pattern here:
https://en.wikipedia.org/wiki/Template_metaprogramming#Static_polymorphism
« Last Edit: October 09, 2015, 11:36:32 am by Reelya »
Logged

jaked122

  • Bay Watcher
  • [PREFSTRING:Lurker tendancies]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8138 on: October 09, 2015, 12:45:48 pm »

--snip--
http://gameprogrammingpatterns.com/flyweight.html
--snip--

I would recommend looking at the wikipedia page on flyweight as well. I liked the use cases on your link, but there are more details on the wikipedia page, and those are important details too.

Have you worked recently with variadic templates? Those are fun, especially for something like working with svg path command arguments in c++, though I'm not sure that this is good style, practice, etc.

So most svg path commands take several arguments of double precision floating points. Some of them take 2, others 4, and some six or more. Writing a function to generate the commands in the structure either requires cumbersome wrapping into various array types, or using what (to me) appears to be an abomination. It looks something like this
Code: [Select]
class path:public element{
  void lineTo(double x,double y)//this is one of a large number of commands.
  {
     addc(FORWARD,true,x,y);
     //update the endpoint of the path.
     position[0]=x;
     position[1]=y;
  }
  void addc(int c,bool starting){}//this is the empty add function
  template<typename T, typename... Args> void addc(int c,bool starting, T value, Args... rest){
     if(starting){
       commands.push_back(c);
     }
     arguments.push_back(value);
     addc(c,false,rest...);
     path_needs_update=true;
   }
};
Now my problem is that I can't figure out if I can limit the types added to double or not using this method.

Also there's the matter that this is written recursively, and I try to avoid that because stack space is an unnecessary expense in most cases. I also don't want to do the C-style variadic parameters because they are... Not safe... in addition to quite a few other issues.

So, do you have any suggestions? Or is this actually okay, since it's not in the visible part of the path class?
« Last Edit: October 09, 2015, 12:55:32 pm by jaked122 »
Logged

breadman

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8139 on: October 09, 2015, 01:31:17 pm »

Hmmm. Can't you just add static members to your classes, then retrieve the static data via overridable accessor methods?

The issue with that is that every single object must then carry around the virtual function pointers for the correct methods. If four virtual functions are needed to get and set two pieces of static data, then that's four 32 bit pointers you've added to each and every object in your simulation. And it's really inflexible and error-prone. If someone extends your heirarchy but forgets to add the static member or virtual functions, then the system breaks without any error messages or warnings.

Something about this sounds wrong to me.  Generally speaking, the most efficient implementation of a class hierarchy will let each instance have one pointer to its class type, which will contain the pointers to the class methods and static members, as well as a pointer to its superclass.  Anything not overridden in a subclass should default to the closest parent class implementation, instead of breaking.

So yes, that's four 32-bit pointers added to each class in the system, but that's a far cry from the massive inefficiency of adding method pointers to each object in the system.  Basically, the compiler already uses something like the flyweight pattern, so creating a second class hierarchy for the methods on top of the compiler's system is a waste of memory, CPU, and effort.  If something can't be changed on a per-instance basis, it's not stored on each instance.

Unless, of course, I'm wrong because the particular language or compiler you've chosen to use is completely insane.

There are cases for manually using a flyweight pattern, but they mostly involve sending data through an API that can't handle the class structure of your language, such as GPU or network code.  In such cases, you'll need either a (possibly virtual) method to assemble the flyweight structure, a class variable to store it, or both, if you choose to use native classes at all.
Logged
Quote from: Kevin Wayne, in r.g.r.n
Is a "diety" the being pictured by one of those extremely skinny aboriginal statues?

Reelya

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8140 on: October 09, 2015, 01:39:31 pm »

Oh you're right - there's one additional member added when you have virtual functions, not multiples. But they still get an extra pointer for the vtable. A custom system still has advantages. Change the pointer during runtime, and change the behavior class of the object. You can't do that at all with the virtual system. Make new behaviours during runtime (e.g. neural networks, genetic algorithms), and link them to the objects seamlessly. Also, by building your own system you can make a master array for all the behaviours. If you don't need > 256 behaviour types, then you don't need a pointer at all. You can use a unsigned char to specify the behaviour of the object, and still get more features than vanilla polymorphism.

~~~

As for the variadic templates: I haven't picked up much from C++11 yet. I learned my lesson a while ago, wait and see which language features "stick" and which ones get forgotten. But I took a quick look at the variadic templates just now

For checking the values, you could make the "first" element of the template double D instead of typename T. That would ensure that any non-doubles in the pipeline should throw a compiler error. The other way would be static asserts, but those are probably overkill since I'm pretty sure just making the first parameter a double will fix it.

I wouldn't worry about the recursion:
http://eli.thegreenplace.net/2014/variadic-templates-in-c/
Quote
If you're concerned with the performance of code that relies on variadic templates, worry not. As there's no actual recursion involved, all we have is a sequence of function calls pre-generated at compile-time. This sequence is, in practice, fairly short (variadic calls with more than 5-6 arguments are rare). Since modern compilers are aggressively inlining code, it's likely to end up being compiled to machine code that has absolutely no function calls. What you end up with, actually, is not unlike loop unrolling.

« Last Edit: October 09, 2015, 01:53:14 pm by Reelya »
Logged

Gatleos

  • Bay Watcher
  • Mournhold... City of Light... City of MAGIC!
    • View Profile
    • Someone Sig This
Re: if self.isCoder(): post() #Programming Thread
« Reply #8141 on: October 09, 2015, 02:45:06 pm »

How about we take the polymorphism and encapsulate that inside our base class? The idea is that each Person object is a container which holds a pointer to a "PersonType" object, and it's these PersonType objects which keep track of all the types.
That seems like it would lead to a lot of
Code: [Select]
if(person.GetTitle() == "ClassA") {
// do a thing
}
else if(person.GetTitle() == "ClassB") {
// do this thing
}
else if(person.GetTitle() == "ClassC") {
// do another thing
}

But now that I think about it, it seems like that might be less work than screwing around with more class definitions. I may try the template method you explained.


For the record, my MASSIVE INHERITANCE HIERARCHY looks like this right now:
Code: [Select]
  A
 /  \
B    C

I have class definitions that look like this:
Code: [Select]
class Entity {
// A pointer to static data
const MapEntityS* sEnt;
int x;
int y;
/*
Basic things like drawing functions
*/
};

class MapUnit : public MapEntity {
// Oh no, not another static data pointer
const MapUnitS* sUnit;
/*
Some pathing stuff
*/
}

class MapSite : public MapEntity {
// Uh oh
const MapSiteS* sSite;
/*
Data and functions specific
to sites
*/
}

For each of those classes, there's a static data class (appended with an S), that is the flyweight. The inheritance hierarchy of those mirrors the one here, and each of these classes contains a pointer to one (which means the child classes have two...) It's already convoluted, and most of it is probably overkill since I may not even need more than MapSite and MapUnit. My master plan was taking advantage of polymorphism with overridden class methods, but then I realized that I sometimes need access to static data associated with one of the child classes... which I can't access if I cram it into a MapEntity pointer.

I can't see any way to clean it up without dropping the inheritance altogether, but then I would need to start duplicating large amounts of data (like animation data that was handled by the parent class). My main problem was in attempting to change the static data pointed to by a MapUnit, which meant I had to change it in the MapEntity too. I know the simplest way to do that, it just feels like I've made things hard on myself with my terrible organization.
Logged
Think of it like Sim City, except with rival mayors that seek to destroy your citizens by arming legions of homeless people and sending them to attack you.
Quote from: Moonshadow101
it would be funny to see babies spontaneously combust
Gat HQ (Sigtext)
++U+U++ // ,.,.@UUUUUUUU

Reelya

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8142 on: October 09, 2015, 02:49:23 pm »

there are no if statements used in what I'm suggesting. As per my example, Each Person would have a member "PersonType *type". PersonType defines some functions, so you just access them by e.g. person->type->DoThing(). and the various type pointers handle the selection. No if statements used. You can also encapsulate that detail inside the Person class by passing the DoThing call through. Then you only need to write person.DoThing() to get polymorphism working. Note that this is polymorphism on a concrete class, no need for an abstract pointer to a base class. You can also pass the "this" pointer through to type's DoThing. Let me outline that with modified code:

Spoiler (click to show/hide)

Now you could make a student by writing:

Person bob(typeStudent);

And you'd just write:

bob.DoThing();

to call the DoThing for bob as a Student. Oh but bob's a teacher now:

bob.type = typeTeacher;

now call DoThing again:

bob.DoThing();

to make bob do the thing as a teacher

BTW, typeTeacher and typeStudent are actually objects not classes. That means each of them could be parameterized. e.g. you could have an int in Teacher for rank. Then you'd instantiate multiple typeTeacher objects with different ranks, e.g. typeTutor, typeTeacher, typePrincipal could all share the code for Teacher. Similarly, there could be separate types for 1st year, 2nd year, 3rd year students, and each student's type is changed as they move up. This saves you from having to have a "year" field for each student, you just point them at the correct typeStudent for their year.
« Last Edit: October 09, 2015, 03:16:30 pm by Reelya »
Logged

jaked122

  • Bay Watcher
  • [PREFSTRING:Lurker tendancies]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8143 on: October 09, 2015, 04:04:19 pm »

--snip--
As for the variadic templates: I haven't picked up much from C++11 yet. I learned my lesson a while ago, wait and see which language features "stick" and which ones get forgotten. But I took a quick look at the variadic templates just now
That doesn't compile for me, but I realized that compiler errors would show up if they weren't something that was at least implicitly converted to double due to adding arguments to a vector of doubles.

Therefore, even bools work correctly(and are necessary). Still something that I wonder about, assigning all numbers other than zero to be true. That seems like a questionable decision typewise, but it does make error codes easy.

Another wonderous feature in the rollercoaster of intention of C and C++ design.

3man75

  • Bay Watcher
  • I will fire this rocket
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #8144 on: October 10, 2015, 09:08:24 pm »

Two big problems with a loop I decided to add to my LDC project. 1. is that i'm having trouble finding a way out of the loop I created. I made it so that when used it would start you off again at inputing the start time again ((as HH:MM)).

The second though sorta minor is that I am getting errors that say that cout is ambigous when I run it in my compiler. It doesn't seem to hurt when it runs or do anything but I'd like to know how to stop it from happening so that I can craft the perfect program. ((That last part was a joke btw)

So my question how do people usually use loops to start again when wanting but without getting trapped within one? I had a user choice var. created so that in the end I can
get the user to input 1 or 2 to either continue or end the program.




Spoiler:  Code (click to show/hide)

EDIT: Sorry I messed up the post.
Logged
Pages: 1 ... 541 542 [543] 544 545 ... 796