ENIGMA Forums

Contributing to ENIGMA => Developing ENIGMA => Topic started by: TheExDeus on July 14, 2014, 06:42:19 am

Title: Accessing with types and passing by strong reference
Post by: TheExDeus on July 14, 2014, 06:42:19 am
I think I have made a topic about this before, but I can't remember, so here it goes again, with some new insights.
As we all know in GM every ID for resources (LGM like sprites, background or runtime like ds_grid) is an integer. I always lowed this in GM, because it makes learning A LOT easier, as in C++ this usually involves pointers or references. But this makes code messy, as the engine doesn't know if the number "5" gives is an ID for sprite or a background. Here are some suggestions:
1) I previously suggested we just use classes (both return and pass to functions), as the user wouldn't know any better. He might be confused why "sprite_id*5" doesn't work, but that shouldn't work in any case. But it does mean problems with variables. By default every variable is a "variant", which means "variant" would have to cast to "Sprite" and "Background" and so on. I don't think that is possible, as it might only cast to "*Sprite" at best, and that would be required for this to work:
Code: (EDL) [Select]
sprite_id = spr_some_guy;We should pass references anyway, as I don't want to copy data in this instance.
2) Create a typedef which basically wraps integers. This seems like the best option and in my ultimate wisdom I didn't come up with it before. Basically we would have a type define as "Sprite_ID" and it would basically be an integer in every shape or form. The only difference is that if I pass a number 5 of type "Sprite_ID", then I know it's a sprite ID, not something else. The problem here is that we still need to cast variant to these types. Like this:
Code: (EDL) [Select]
a = 5; //Here variant of type variant with value 5
a = spr_some_guy; //Here the variant needs to be cast, which I don't know if is even possible taking into account the POD nature
Also, this would mean we ALWAYS would have to use sprite name or the ID returned by sprite_add, to reference this sprite. This means we couldn't just load an integer from a file, and then use it as an ID. But the user shouldn't be doing that anyway, as he cannot know which ID the sprite will be assigned to. So I think we are okay in this regard. Slightly break GM compatibility, but greatly increases the future prospects we could have. Like we could replace draw_sprite, draw_background and draw_surface with one function - draw_image. And we could even allow draw_sprite's image frame argument to be supported via an overload.
3) Another way which would only partly fix this, is to have a unified ID vector. Like all sprites, backgrounds, rooms, instances etc. would use the same map or vector. This would mean every ID would be 100% unique, and thus identifiable with a certain kind of resource. But this greatly reduces the maximum of amount of resources possible (maximum integer can hold, which might not be enough if they include all instance of all rooms and even all tiles).

But I don't really suggest totally getting rid of all those functions. I think they can stay for the time being as they are required for GM compatibility, and we are quite used to them. What I actually need this functionality for is new stuff, especially extensions. Like I am in process of making a GUI extension (something on the lines mentioned in the wiki here: http://enigma-dev.org/docs/Wiki/GUI_Functions) and I want to support these two case:
Code: (EDL) [Select]
button_set_image(button,spr_button1);
button_set_image_hover(button,bg_button1);
Instead of:
Code: (EDL) [Select]
button_set_image_sprite(button,spr_button1);
button_set_image_hover_background(button,bg_button1);
And the second case is with my own classes:
Code: (EDL) [Select]
button_set_parent(button, window1);
window_set_parent(window1, window2);
Instead of bazillion variants like this:
Code: (EDL) [Select]
button_set_parent_window(button, window1);
window_set_parent_window(window1, window2);
So basically I want the ID returned by functions like button_create(), not to be an integer without any type information, but identifiable as an instance of a class.

One way to fix this would be to ask users to code in C++. So they could in fact write:
Code: (EDL) [Select]
Button button1 = button_create();And then use "button1" everywhere as it's a C++ class. But I don't want that. I want the user to be able, to write:
Code: (EDL) [Select]
button1 = button_create();
In my case maybe the third option could work, as I basically would have a GUI_MAP having all the GUI elements which would mean that every one will have a unique ID. But I wouldn't be able to use this for other resources like sprites or backgrounds.

So this topic is mostly addressed to Josh, as he deals with EDL. How would you propose to fix this?
Title: Re: Accessing with types and passing by strong reference
Post by: sorlok_reaves on July 14, 2014, 10:19:37 am
This would have the added benefit of fixing a weird interaction with "assume default variables are 0" and having identifiers just be integers. So this would be a great feature from my point of view!

1) I previously suggested we just use classes (both return and pass to functions), as the user wouldn't know any better. He might be confused why "sprite_id*5" doesn't work, but that shouldn't work in any case. But it does mean problems with variables. By default every variable is a "variant", which means "variant" would have to cast to "Sprite" and "Background" and so on. I don't think that is possible, as it might only cast to "*Sprite" at best, and that would be required for this to work

variants (or possibly vars?) contain a "type" in addition to a value. So storing a "class sprite_id" in a variant can be done by storing the integer value in the variant's "value", and storing an enum (sprite_id) in the variant's type. Then, when casting to a "sprite_id", you can fail (or return -1) if the types don't match.


Also, this would mean we ALWAYS would have to use sprite name or the ID returned by sprite_add, to reference this sprite. This means we couldn't just load an integer from a file, and then use it as an ID. But the user shouldn't be doing that anyway, as he cannot know which ID the sprite will be assigned to.

Would this prevent external music files from working correctly? Those are often distributed with a game in a separate directory, and loaded dynamically at runtime. (I may be misunderstanding how your typedef solution is supposed to work.)
Title: Re: Accessing with types and passing by strong reference
Post by: Rusky on July 14, 2014, 10:21:02 am
typedefs are interchangeable- it would have to be a struct with an int member to actually enforce the types. It would be interesting to add the resource types to the possibilities for variant rather than casting everything.
Title: Re: Accessing with types and passing by strong reference
Post by: Darkstar2 on July 14, 2014, 12:15:33 pm
You've mentioned the implications on reducing / limiting resources available to use (instances, etc.)  This could have an impact for larger games development, can't we keep the existing functions/method and add a new set in parallel or your last C++ suggestion is good, and people could create their wrappers ?

Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on July 14, 2014, 12:43:33 pm
Quote
Would this prevent external music files from working correctly? Those are often distributed with a game in a separate directory, and loaded dynamically at runtime. (I may be misunderstanding how your typedef solution is supposed to work.)
If you use sound_add() or something, then it would return Sound_ID, so there wouldn't be a problem. What I meant was that if you load something that returns an integer, then you wouldn't be able to use it as a resource without specific casting. I guess you could still always do "sound = (Sound_ID)5;". But this shouldn't really be a problem, because any function that actually creates or adds a resource, should return the right type.

Quote
You've mentioned the implications on reducing / limiting resources available to use (instances, etc.)
It's only valid for the third solution. Of course don't get me wrong, 32bit int can store from 0 to 4,294,967,295 - 64bit can store 0 to 18,446,744,073,709,551,615. Those numbers are so large, it's very improbable you would have issues. Problems could arise if we port to some embedded devices where int maybe only 16bits (though I don't know any except microcontrollers which ENIGMA will never support), then you could only have 0 to 65,535. But as I mentioned in the post - this solution doesn't work on custom resources, because then we would require a much complex system that allows adding stuff from extensions to this container. It also removes (or rather complicates) some possibilities for threaded resource management, as multiple threads would use the same container.

Quote
typedefs are interchangeable- it would have to be a struct with an int member to actually enforce the types. It would be interesting to add the resource types to the possibilities for variant rather than casting everything.
True, compiler will not error for wrong types. Maybe we only need EDL parser to error at this? But that is why I am waiting Josh's input on this. He would probably have to add it too.
Title: Re: Accessing with types and passing by strong reference
Post by: Darkstar2 on July 14, 2014, 02:16:26 pm
In any case I have some plans on my own for ENIGMA for large game developers, I am planning to add dynamic resources handling, packing/unpacking resources from external files directly in memory, and every aspect of the game including rooms etc to outside files, making possible multi GB games and IDE remaining compact and lightning fast.

So in theory the limitations would be for instances/resources present in memory, once external resource handling is into play all that would be irrelevant.  That is the system used by nearly all commercial games now anyway.

As far as threaded resources management, that is exactly where the dynamic resource handling falls under. Ability for such developers to use progression bars or dynamic loading screens, with animation, progression, etc whilst the resource(s) is/are being loaded.
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on July 14, 2014, 05:36:57 pm
It doesn't matter where the resources reside. You still need to have a way to reference them. And you do it via an ID. That is what this topic is about.
Title: Re: Accessing with types and passing by strong reference
Post by: sorlok_reaves on July 14, 2014, 08:42:02 pm
If you use sound_add() or something, then it would return Sound_ID, so there wouldn't be a problem. What I meant was that if you load something that returns an integer, then you wouldn't be able to use it as a resource without specific casting. I guess you could still always do "sound = (Sound_ID)5;". But this shouldn't really be a problem, because any function that actually creates or adds a resource, should return the right type.

Ok, that makes sense.
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on July 25, 2014, 02:07:34 pm
So does Josh have an opinion on this? Should we start ditching ID's and maps, and start passing classes?
Title: Re: Accessing with types and passing by strong reference
Post by: Goombert on July 25, 2014, 03:14:50 pm
Studio returns pointers for a lot of resources now. One of these resources turned into pointers were textures, the reason is because people always had to use -1 to specify no texture on a model. I really don't get why they just didn't overload the function, but w/e.
http://gmc.yoyogames.com/index.php?showtopic=609599
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on July 25, 2014, 04:12:23 pm
I think we should use it for everything. But we must overload the default variable type (variant or var) to support that. Because then at least I will be able to figure out what is actually passed to a function. GM:S seem to have a "ptr" type specific to textures. So you cannot use it for anything else, because it's not really descriptive. What I mean is actually less to do with regular pointers, but to references. I want textures to be "Texture", so when I make a function "draw_texture(Texture &texture, gs_scalar x, gs_scalar y){}" it would error if I pass a sprite, background, object, sound or anything else.
Title: Re: Accessing with types and passing by strong reference
Post by: Goombert on July 25, 2014, 08:28:40 pm
Yeah but that partially defeats the purpose of a variant type for what you want to use it for.
Title: Re: Accessing with types and passing by strong reference
Post by: Rusky on July 25, 2014, 08:41:56 pm
Make variants for all the resources then.
Title: Re: Accessing with types and passing by strong reference
Post by: Josh @ Dreamland on July 25, 2014, 09:45:16 pm
Yes and no.

For things you create arbitrarily, you are right—these could be greatly benefited by classes. I would probably have it that var would not be allowed to represent these classes, except possibly through the C# approach. There's no denying how extremely useful it would be to have object-oriented file, UI, and data structure code.

Instances, you are beginning to push the limit. It is useful to be able to say, at very least for the purpose of static analysis, that other is  an obj_wall, or that instance_nearest(x, y, obj_missile) is an obj_missile. But do we really want to have you check inst instanceOf obj_missile, then cast? I'd hear an argument either way.

Resources, you have hit the limit. There is little reason to ever have spr_some_guy be anything but an integer. Let's choose a raw, gaping sore in GML as an example: sounds. Users have always been plagued by how large their audio resources end up being in GM. In the golden days, large audio files corrupted game files left and right, and even when they did not, insane save times kicked in. Pissed-off users would eventually be driven to store their sounds externally. We just recently got bitten in the ass by Round II of this issue as it became clear that optimizing sound storage for insert instead of access was the better move. This is wrong on so many levels.

The beauty of the resource tree is that your resources are managed for you—you don't need to know anything about where, when, or how sound0 was loaded. You just play it and it works. On your side, it's an integer; you can't get any simpler an opaque pointer than that. Behind the scenes, that sound may or may not be loaded, or actually even exist. We can replace this mechanism with some sort of weak reference class, but why would we? An integer serves the purpose fine.

I wouldn't argue against the use of, eg, typedef size_t sprite_t; to allow us to offer object-oriented features for these resources. But then, does spr.exists() really make any sense?

Just some considerations. I would be fine with a statically typed var of some sort, honestly.
Title: Re: Accessing with types and passing by strong reference
Post by: Rusky on July 25, 2014, 10:49:44 pm
Using integers for resources is anything but opaque. The whole point of this exercise would be to separate resources into different types so you don't accidentally use one when you mean the other. With integers, you get weird behavior; with tracking them, you get an error message instead. That's one of the biggest reasons to use a pointer internally for textures- it's too easy for people to accidentally use a sprite or background instead and have it work in simple examples and break for no apparent reason later on.
Title: Re: Accessing with types and passing by strong reference
Post by: Josh @ Dreamland on July 25, 2014, 11:10:19 pm
Pointers are just sparse integers. Yes, it's easy to obtain the next sprite given the current one, because usually you just add one. Well, let's say we handed around raw pointers. Now you can usually obtain the next sprite given the current one by adding sizeof spritestruct. The only difference is that you'll get an access violation and kill your program if you're wrong in the pointer case. As I stated, I'm fine with having a typedef of size_t for each resource kind. This would be a hint to the compiler that it is not to be used in arithmetic. It could also be used to allow object-oriented programming around that type; eg, replace spr_whatever.draw(0, x, y) with draw_sprite(spr_whatever, 0, x, y). There only difference is that this "pointer" is small, dense, and managed by us.
Title: Re: Accessing with types and passing by strong reference
Post by: Rusky on July 25, 2014, 11:16:22 pm
I'm not talking about the actual content bits, in that sense it doesn't matter if you use ints or Something*s. I'm talking about the type flag in variant- rather than just typedef'ing size_t (which the compiler just ignores for type checking purposes without so much as a warning), associate some actual type information with the different resource spaces.

There's two ways to do that- at compile time, with a wrapper struct rather than a typedef; and as another case of variant, which would be a drop-in replacement for GM's system but with the ability to catch more errors.
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on July 26, 2014, 08:43:33 am
Quote
Pointers are just sparse integers. Yes, it's easy to obtain the next sprite given the current one, because usually you just add one. Well, let's say we handed around raw pointers. Now you can usually obtain the next sprite given the current one by adding sizeof spritestruct. The only difference is that you'll get an access violation and kill your program if you're wrong in the pointer case. As I stated, I'm fine with having a typedef of size_t for each resource kind. This would be a hint to the compiler that it is not to be used in arithmetic. It could also be used to allow object-oriented programming around that type; eg, replace spr_whatever.draw(0, x, y) with draw_sprite(spr_whatever, 0, x, y). There only difference is that this "pointer" is small, dense, and managed by us.
I don't want "spr_whatever.draw()" or any other function like that at all. I don't want spr_whatever to be a class with members. I want that when I pass spr_whatever (with ID = 2, for example) to a draw_background(), I would know that it's incorrect, and that I am not in fact drawing background with ID = 2. So I want it to trow an error, as well as allow me create more generic functions. Like draw_image() which would take both sprite and background. Or replace sprite_get_texture, background_get_texture and surface_get_texture with one function - get_texture(). Or even go one step further and allow draw_primitive_texture() to take sprites, backgrounds and surfaces directly, without specific query for the texture. So I want to be able to figure out what the hell I am passing to the functions, instead of just an "int".
So I also recommended the typedef for this, but how would casting work when using default variable types? Like this this code:
Code: (EDL) [Select]
a = 5; //Here variant of type variant with value 5
a = spr_some_guy; //Now "a" need to be typdef'd type "Sprite"

Also, as I mentioned before - doing arithmetic with ID's is retarded. We should not encourage it, and if possible, deny that possibility.
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on October 03, 2014, 07:09:34 pm
Quote
Resources, you have hit the limit. There is little reason to ever have spr_some_guy be anything but an integer.
Things like draw_image() would be possible, which would draw a sprite, a background, a texture or a surface depending on what is given. I personally would like that. resource_exist() would also then work like that. Having an identifier without the resource attached would still be possible, and that identifier actually doesn't need to be more than an int. It just has to be an int, which can be differentiated from other int's. That is where my typedef idea comes from. All of this also gives a lot better error handling, as Rusky says. If you have hundreds of resources in a game, then it's actually very likely that any combination of resource types will not error, when they actually should. Like if you have 100 sprites, then draw_sprite(snd_hurt,...) will work if there is less than 100 sounds. So the current error handling cannot catch that at all.

Quote
But then, does spr.exists() really make any sense?
It would, just like sprite_exists(spr) makes sense right now. The problem is how to cast one thing to another. We can make sure that sprite_add() returns sprite_t, and we can make sure sprites in LGM are cast to it, but when do assignments and comparisons (which don't actually make sense), we could get into trouble.

I would really get to the bottom of this now, as I'm still making the GUI extension and as GL fixes come closer to finish, I would like this to be done as well.

edit: There is a discussion about strong type checking here: http://stackoverflow.com/questions/376452/enforce-strong-type-checking-in-c-type-strictness-for-typedefs . There are also some propositions, like the Microsoft variant, or the C++11 enum classes.
Title: Re: Accessing with types and passing by strong reference
Post by: Josh @ Dreamland on October 05, 2014, 01:25:16 pm
I'm trying to be defensive on one front and am over-defending another. What you are asking me for is type safety, and yes, we can get that. What you're also asking me for in the process is to not use an integer, and that would be fallacy. I stand by my original statement—integers as our reference are opaque. The fact that you have reference collisions does not make the data any less opaque. The point of integers is that they're dense and easy to track—it's trivial to check if a sprite is loaded given its index, and it's harmless to be wrong about whether it's still loaded.

Yes, collisions happen all the fucking time, and when concepts are genuinely confusing—eg, I asked you for a texture index, you passed me a sprite index or a background index—head-scratching behavior can occur.

We don't have the framing right now to really lick this. How about this:  In the interim, why don't you replace int in these functions with sprite_t, background_t, path_t, etc. For now, please just use typedef int sprite_t;, etc.



Here's where we run into trouble: you are also asking me for overloads. There's a lot more involved in overloading widget_set_parent(button_t button, window_t window) with widget_set_parent(window_t window1, window_t window2) than you are giving credit for. The compiler can generate an array for the cross-product of all these overloads, if it also generates RTTI metadata for them, but then you still have new problems.

Your first problem is going to be that C++ won't let you overload these integer types. Since they're all integers, you can't create this overload. You can get around this by using a struct instead of a typedef. In doing this, you have created the problem that you can't store these in integers at all, anymore, which may be what you want. If you do this, I recommend having these all inherit a class called reference_index which just stores an integer. This will allow you to roughly implement this alongside the current variant implementation, with a little tweaking.

The second problem is arguably worse. All this sounds great until you realize that you have just moved the problem of overload resolution to runtime.

So let's assume you've overloaded widget_set_parent for a number of different types. You'll have something like this:

Code: (C++) [Select]
struct window_t: reference_index { /* ... */ }
struct button_t: reference_index { /* ... */ }

void widget_set_parent(window_t window, window_t parent);
void widget_set_parent(button_t button, window_t parent);

Naively, you can give the user methods to "cast" a variant between those. To involve the compiler is to introduce our run-time compile errors. We tell the compiler that it's okay for a user to pass variant to these functions. To enable this to happen, the compiler will generate a run-time type enumeration for variant, like so:
Code: (C++) [Select]
namespace enigma {
  enum variant_runtime_types {
    VRT_SPRITE,
    VRT_BACKGROUND,
    // ...
    VRT_WINDOW,
    VRT_BUTTON,
    VRT_COMBOBOX,
    // ...
  }
}

It can do this, for example, by querying for members structs of enigma_user which extend reference_index. No problem. It will also generate special methods to fetch these, as needed, and we'll assume
I have pasted a sample execution of the idea (http://pastebin.com/5U4cLkgN) to Pastebin. It shows the basic stages of this, but does not show a complete variant, nor the cast method. But the cast method looks very similar to the construct method, only it actually checks the value of variant.rtti before returning. In practice, we'll probably replace the template function I showed there with a structure containing those so that we don't generate linker errors in problem scenarios (the missing information will be caught at compile time).

This is fine and dandy, but the C++ compiler can't tell which type a variant should use, because a variant can cast to any of those. Thus, all of those methods are weighted equally to the overload resolving compiler. We have lost compile-time overload checking, which is pretty much nothing new, I suppose. But now the compiler has to deal with this to allow it to happen at all.

To do this, the compiler must first identify functions whose overloads are ambiguous to variant in ISO C++. This is a painful check, but it's doable. The compiler must then generate overloads taking const variant&/const var& for these types. This requires two pieces.

First, we need to declare a place for runtime overload disambiguators:
Code: (C++) [Select]
// Runtime overload disambiguation
map<tuple<int>, void(*)(variant, variant)> widget_set_parent$_overloads;
static void widget_set_parent$button$window(variant arg0, variant arg1) {
  widget_set_parent((button_t)arg0, (window_t)arg1);
}
static void widget_set_parent$window$window(variant arg0, variant arg1) {
  widget_set_parent((window_t)arg0, (window_t)arg1);
}
static inline void widget_set_parent$_disambiguate(const variant& arg0, const variant& arg1) {
  map<tuple<int>, void(*)(variant, variant)>::iterator it = widget_set_parent$_overloads.find(tuple<int>(arg0.rtti, arg1.rtti));
  if (it != widget_set_parent$_overloads.end())
    return it->second(arg0, arg1);
  show_error("No overload for widget_set_parent(" + rtti_names[arg0.rtti] + ", " + rtti_names[arg1.rtti] + ")");
  return void();
}

The above code assumes we also export the names of these types into an array somewhere, which is also dastardly ugly. It also assumes the existence of a tuple class which is basically a vector that I can construct really easily to save code. :P

Then, we need to populate that map at load time:
Code: (C++) [Select]
void load_overload_disambiguators() {
  widget_set_parent$_overloads[tuple<int>(VRT_BUTTON, VRT_WINDOW)] = widget_set_parent$button$window;
  widget_set_parent$_overloads[tuple<int>(VRT_WINDOW, VRT_WINDOW)] = widget_set_parent$window$window;
}

And if you want implicit casting, well, you're looking at even more logic generated for the overload disambiguation routine. Coupled with even more metaprogramming-fueled metadata.

But anyway, now you have a very thorough synopsis of how I'd handle it. I imagine if I don't get around to it now, I'll get around to it after you all sit on it for three years and I've long forgotten it's a problem. :P
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on October 05, 2014, 02:53:38 pm
That requires a lot of compiler changes and is meant to create different functions for different resources. Which might also be needed in the future, but in my extension, I actually need only to check inside the function what type is given. Like something of this sort would suffice to me:
Code: (C) [Select]
        #include <typeinfo>
        typedef int sprite_t;
        typedef int background_t;
        template<typename T>
        int widget_set_background(T image)
        {
                if (typeid(image) == sprite_t)) use_sprite();
                else if (typeid(image) == background_t)) use_background();
                else printf("Not a valid type give! Either don't do anything, or just default to sprite or something!");
         }
This is sadly not working, because typedef doesn't mean anything to the compiler. typeid(sprite_t) is just an integer. So I cannot differentiate. Strict typedef's for error checking of course would also be useful, as we discussed in this topic. But C++ doesn't allow that either, so just a "typedef" wouldn't solve our problems. But here I don't even want that either. As this will be an extension, then I don't want to overhaul the whole ENIGMA engine to accommodate it. So the only thing I want is the power to figure out the type inside the function.

If nothing else, then how at least I can do this for the custom objects I'm making now? Like supporting sprite_t and background_t is also going to be a monumental change it seems, so what would be the best way to support a "button" and a "window"? If EDL actually allowed C++ pointers, then I would probably just use those. Or actual instances of classes, because I hate pointers.
Title: Re: Accessing with types and passing by strong reference
Post by: Josh @ Dreamland on October 05, 2014, 03:36:47 pm
Using typeid is dangerous as its behavior is implementation-defined. A compiler could easily tell you that typeid(T).name() is "T". Were that not the case, you could implement the above directly without use of the ENIGMA compiler—you would accept derived_reference_index<T> and then compare typeid(T).name() against typeid(enigma::RTTI::background_t).name(). Nothing you do will make this operation not require surgery on how variant stores its internals, and major surgery on how the engine passes around types.
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on October 05, 2014, 04:56:36 pm
I'm screwed then. I can guarantee I won't be able to change the variant or the compiler to allow this. Most I can hope for is the fact I can maybe use classes now. Then the extension will work a lot differently then the rest of ENIGMA, but still. I don't want a hundred functions just to set a freaking background image.
Title: Re: Accessing with types and passing by strong reference
Post by: Josh @ Dreamland on October 05, 2014, 05:02:20 pm
The pastebin link (http://pastebin.com/5U4cLkgN) basically tells you exactly how to implement this for your own functions. :P

You don't have to add the cast to variant, but it shows you how to do that, too.
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on October 06, 2014, 05:02:20 am
So I can hardcode the compiler codegen as well?
Also, the standard say's _t is reserved, so I guess it's better not to call them window_t.

I'll try using your code then. Though I really have to think hard to understand it. :D
Title: Re: Accessing with types and passing by strong reference
Post by: Josh @ Dreamland on October 12, 2014, 06:41:28 pm
The standard says no such thing, but a quick googling shows that POSIX decided to reserve them. I don't really fear the wrath of POSIX reservations, but you can name them how you choose. Try to find a consistent naming scheme—I liked _t, personally, and would still argue for it on the grounds that no standard header has business using type names such as window_t. That said, we can expose them to ENIGMA under whatever naming convention we like, so really I'll only debate you at the enigma_user level.

The method uses empty classes by given names as keys for template specialization—it's an old hack that doesn't show up a whole lot. Template specializations are use to look up type metadata, which is used to tell variant what it holds.
Title: Re: Accessing with types and passing by strong reference
Post by: TheExDeus on November 05, 2014, 09:09:23 am
I can't get this to work in ENIGMA. I added this to var4.h:
Code: [Select]
//For RTTI
namespace enigma{
  struct reference_index {
    int id;
    reference_index(int idx): id(idx) {}
    enum { DNE = -1 };
  };
 
  template<class T> struct derived_reference_index: reference_index {
    derived_reference_index(): reference_index(-1) {}
    derived_reference_index(int idx): reference_index(idx) {}
  };
 
  namespace RTTI {
    template<class T> int compiler_ids();
  }
}

struct variant
{
template<class T> variant(const enigma::derived_reference_index<T>& dri): rval(dri.id), sval( ), type(enigma::vt_real), rtti(enigma::RTTI::compiler_ids<T>()) { }
}
Then I modified var4.cpp to have this:
Code: [Select]
variant::variant(const variant& x): rval(x.rval.d), sval(x.sval), type(x.type), rtti(x.rtti) { }
variant::variant(const var& x): rval((*x).rval.d), sval((*x).sval), type((*x).type), rtti((*x).rtti) { }
variant::variant(): rval(0), sval( ), type(default_type), rtti(-1) { }
In my extension's include.h:
Code: [Select]
namespace enigma {
  namespace RTTI {
    enum variant_runtime_types {
      VRT_WINDOW, 
      VRT_BUTTON
    };
   
    //template<> int compiler_ids<enigma_user::window_t>() { return VRT_WINDOW; }
    template<> int compiler_ids<enigma_user::button_t>() { return VRT_BUTTON; }
  }
}
And button create:
Code: [Select]
variant gui_button_create(){
if (gui::gui_bound_skin == -1){ //Add default one
gui::gui_buttons.insert(pair<unsigned int, gui::gui_button >(gui::gui_buttons_maxid, gui::gui_button()));
}else{
gui::gui_buttons.insert(pair<unsigned int, gui::gui_button >(gui::gui_buttons_maxid, gui::gui_buttons[gui::gui_skins[gui::gui_bound_skin].button_style]));
}
gui::gui_buttons[gui::gui_buttons_maxid].visible = true;
gui::gui_buttons[gui::gui_buttons_maxid].id = gui::gui_buttons_maxid;
return variant(enigma::RTTI::button_t(gui::gui_buttons_maxid++));
}
And I get this:
Code: [Select]
Universal_System/Extensions/BasicGUI/buttons.cpp: In function 'variant enigma_user::gui_button_create(gs_scalar, gs_scalar, gs_scalar, gs_scalar, std::string)':
Universal_System/Extensions/BasicGUI/buttons.cpp:195:65: error: no matching function for call to 'enigma::RTTI::button_t::button_t(unsigned int)'
   return variant(enigma::RTTI::button_t(gui::gui_buttons_maxid++));
                                                                 ^
Universal_System/Extensions/BasicGUI/buttons.cpp:195:65: note: candidates are:
In file included from Universal_System/Extensions/BasicGUI/buttons.cpp:34:0:
Universal_System/Extensions/BasicGUI/include.h:33:12: note: enigma::RTTI::button_t::button_t()
Among a lot of other things. I have tried about 3 hours to fix this without luck. The best I had was "multiple definitions" linker error.

The problem is that your example is self contained, but in ENIGMA I need the var4 changes to work even if my extension is disabled. And as nothing is generated right now, then I need to add this manually in extensions instead of main ENIGMA:
Code: [Select]
namespace enigma {
  namespace RTTI {
    enum variant_runtime_types {
      VRT_WINDOW, 
      VRT_BUTTON
    };
   
    //template<> int compiler_ids<enigma_user::window_t>() { return VRT_WINDOW; }
    template<> int compiler_ids<enigma_user::button_t>() { return VRT_BUTTON; }
  }
}
It seems for now I will make the extension without this, but I really would want this. That would greatly reduce my function count, as I would have "gui_element_sprite" instead of "gui_button_sprite", "gui_window_sprite", "gui_slider_sprite" and so on.