Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation of properties could be improved #8

Closed
gamecentric opened this issue Sep 3, 2019 · 10 comments
Closed

Documentation of properties could be improved #8

gamecentric opened this issue Sep 3, 2019 · 10 comments
Assignees
Labels
triage Pending issue, PR or whatever

Comments

@gamecentric
Copy link

There are a couple of issues in the documentation of properties. First, the following example is provided:

meta::reflect<my_type>("reflected", std::make_pair("tooltip", "message"));

In this case, the first element of the pair, which is used to identify the property, will decay to a const char* and then stored in a meta::any object. Subsequently, properties are found applying operator == on meta::any instances, which is implemented by comparing the value of the pointers. This is a problem, because string literals potentially all have unique addresses, so the property cannot be portably and reliably be retrieved at later time.

The retrieval example is also problematic:

meta::prop prop = meta::resolve<my_type>().prop("tooltip");

since this doesn't compile on at least one compiler (Visual Studio 2019). The problem is that in this case the literal string does not decay to a pointer and apparently the meta::any constructor is not able to accept a const char (&)[8].

These two issues could be solved by using std::string as the type of the property key. However, the use case described in the documentation seems legit and useful, so if that's the intent, we should fix the code instead so that it works as described. There may be multiple ways to achieve this, for example, we could add the following non-template overload of the meta::type::prop method:

       const auto* curr = internal::find_if<&internal::type_node::prop>([key](auto* candidate) {
          auto ptr = candidate->key().try_cast<const char*>();
          return ptr && strcmp(*ptr, key) == 0;
       }, node);

       return curr ? curr->clazz() : meta::prop{};
    }```
@skypjack skypjack self-assigned this Sep 3, 2019
@skypjack skypjack added the triage Pending issue, PR or whatever label Sep 3, 2019
@skypjack
Copy link
Owner

skypjack commented Sep 3, 2019

As stated in the README, this is a standalone version of the meta subdir from EnTT. The doc is similar too and this is something that would work with EnTT but that cannot work with this library. It's due to the fact that here the identifiers are actual strings and the library hashes them under the hood while in the original version the identifiers are literally numbers (usually hashed strings).

Probably the best bet is to use the model of EnTT for this standalone version as well. strcmp isn't the fastest thing ever and definitely something to avoid using massively in production. Am I wrong?
The drawback is that the name won't be available anymore because we cannot get the original string from its hash. However one can still re-add it as a property when needed.

@gamecentric
Copy link
Author

Of course, using strcmp is not the fastest approach but at least it's guaranteed to be correct and reliable, while using a hash always exposes you to the risk of a hash collision. If the user wants to use hashes for best performance, he/she could just declare a custom literal operator "_hash" and write "hello"_hash instead of just "hello". OTOH, if you used hash internally, the user would have no way to get back to strcmp, if desired. The hash approach is is correct in EnTT since meta is not directly exposed to the user. However, when designing a standalone library, my rule of thumb is "between two approaches, one suboptimal but correct and one optimal with caveats, the default should be the correct one". That said, I agree with you that using strings for id is not the best things for performances and would use a different kind of ids in production.

@skypjack
Copy link
Owner

skypjack commented Sep 4, 2019

I'd use integers for the identifiers to be honest. They are pretty handy: you can use hashed strings ofc, but also runtime or compile-time generated ids as well as identifiers obtained through a pre-processing step. Moreover, they are definitely by far faster than comparing strings.
In this case I don't consider hash collisions a problem though, neither for the types nor for the properties. We have a target space that is much larger than what you need for this kind of tool usually. Moreover, strings here are user defined, not something random that we receive from an external source on which we have no control. Long story short: I never hit a collision but I'd have no problems to flip a character in this case! :-)

That said, the idea of setting the name of the type from its id hasn't revealed a good choice.
First of all, most of the time I don't care of the name and therefore I'm just wasting a pointer for literally nothing. Moreover, the library allows you to set the name as a property in case you need it, so an alternative exists. Finally, ids and names aren't always the same, there are cases in which I use numeric ids that have nothing to do with actual names (when I use meta through EnTT, because it decouples the two things).

So, to sum up, rather than updating the doc to reflect the current behavior, I'd change the latter to make it closer to that of EnTT, that is more correct from my point of view.
What do you think about?

@gamecentric
Copy link
Author

I agree that integers are better for the task of identifying properties, but I believe enumerators are even better. They behave like integers but they automatically also acts as a sort of "namespace", as enumerators from different enumerations do not clash even if they have the same numerical value. If I'm not mistaken you use enums in the test suite, which is good.

I have to trust you if you say the EnTT approach is better and I see no problem if you decide to go that way. The second best alternative remains to just rewrite the docs to use enums instead of strings.

@skypjack
Copy link
Owner

skypjack commented Sep 5, 2019

but I believe enumerators are even better

The only problem with enums here is that we don't know in advance what type the user is going to use. On the other side, reflect<T>(??) need an actual type for ??.
By the way, both old fashioned enums and enum classes can be converted to/from integers. Therefore to use integers in place of strings means that we can use also enums for the identifier for our meta types other than an hashed strings and the others.

Does it make sense?

@gamecentric
Copy link
Author

I lost you. Allow me to rephrase. I was referring to the following idiom, similar to the one that meta already uses in the test suite:

enum class properties {
    nice_property = 0,
    another_property = 1
};
int main()
{
    meta::reflect<MyType>("MyType", std::make_pair(properties::nice_property, 42)) /*...*/;
    auto prop = meta::resolve<MyType>().prop(properties::nice_property);
    assert(prop.value() == 42);
}

I could have used int instead of the property enum, std::make_pair(0, 42) to set the property and .prop(0) to retrieve it, but using an enum makes the code self-describing and avoids collisions with a potential different set of properties. What is the problem you see with this approach?

@skypjack
Copy link
Owner

skypjack commented Sep 6, 2019

Oh, sorry, I thought we were speaking of the names for the meta types, like:

meta::reflect<MyType>(MyEnum::MyValue)

Instead of:

meta::reflect<MyType>(hash("MyName"))

Enums already work like a charm with properties afaik. 👍


So, to sum up, the idea is to use integers (hashed strings or whatever, it's up to the user to decide it) for meta types' names, as it happens in EnTT. On the other side, properties already work and I should only update the doc to add a note about using strings with them. Right?

@gamecentric
Copy link
Author

Enums already work like a charm with properties afaik. 👍

Yes, they do!

So, to sum up, the idea is to use integers (hashed strings or whatever, it's up to the user to decide it) for meta types' names, as it happens in EnTT. On the other side, properties already work and I should only update the doc to add a note about using strings with them. Right?

That's a good plan. I like it.

@skypjack
Copy link
Owner

skypjack commented Sep 7, 2019

I've pushed the version that uses numeric identifiers on the branch wip.
I've still to review the documentation, but the code is there in its final version.
Any comment?

--- EDIT

I've updated also the README file. If you don't spot errors, I'll merge everything on master on Monday or so. 👍

@gamecentric
Copy link
Author

I checked the wip branch and it seems good to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage Pending issue, PR or whatever
Projects
None yet
Development

No branches or pull requests

2 participants