Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Re:creation - a top down action adventure about undeads [hiatus]  (Read 501679 times)

0 Members and 7 Guests are viewing this topic.

Carlos Augusto Br Cpp

  • Newbie
  • *
  • Posts: 40
  • Programming is life
    • View Profile
    • Email
Re:creation - a top down action adventure about undeads
« Reply #585 on: May 14, 2016, 04:10:58 pm »
Thanks for your supporting..I really appreciate this...btw...any expectative of the release date(if this is not a secret :) )
I want soo much to play this game...the game had joypad support?
« Last Edit: May 14, 2016, 08:25:29 pm by Laurent »

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #586 on: May 14, 2016, 08:28:43 pm »
Thanks for your supporting..I really appreciate this...btw...any expectative of the release date(if this is not a secret :) )
I want soo much to play this game...the game had joypad support?
You're welcome. I have no idea how soon the game will come out (probably will take more than 1-2 years).
Yes, it has a joypad support, in fact it was pretty easy to write with SFML.
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re:creation - a top down action adventure about undeads
« Reply #587 on: May 14, 2016, 11:20:48 pm »
I just had see Elias talking about threads and I don't know what are this...
Multiple threads of execution all executing concurrently (so you need to be careful and synchronize access to resources).

I'm a very beginner in programming...
Then you probably want to stay far away from threads for a fair bit of time.

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #588 on: May 14, 2016, 11:23:17 pm »
Jesper Juhl, I was talking about forum threads, not other threads, so it's alright.

Progress update! (very excited about it)

A long time ago I've written serialization system which was used for my Qt animation editor. But now I've expanded it immensely and remade most of it in ImGui! (still hard to believe I did it in just one day)

Here's how serialization code looks:
void Animation::registerClass(MetaBuilder<Animation>& builder)
{
    builder.add("name", &Animation::name);
    builder.add("looped", &Animation::looped);
    builder.add("frameCount", &Animation::frameCount);
    builder.add("frameRect", &Animation::frameRect);
    builder.add("frameTime", &Animation::frameTime);
    builder.add("offset", &Animation::offset);
    builder.add("offsets", &Animation::offsets);
    builder.add("frames", &Animation::frameRects);
    builder.add("sounds", &Animation::soundInfo);
}
The ImGui window is composed by using info from Meta<T> class and each type has specialized element for displaying and editing it! (Bools have checkboxes, sf::Time has InputInt with ms label etc.)

But that's not all. I've previously had some problems with circular dependencies which prevented me from making recursive serialization, but now I've solved it! I can also serialize some STL containers (vector, map, unordered_map) and some SFML stuff (Vector2<T>, Time, Rect<T>)

Here's what I can do. Suppose I have a classes A and B.
struct A {
   std::unordered_map<std::string,B> bs;
}

struct B {
    std::string name;
    int age;
}

Here's how A registered:
builder.add("bs", &A::bs);
And here's how B registered:
builder.add("name", &B::name);
builder.add("age", &B::age);

Suppose there's an instance of A like this:
A a;
a.bs = {
    "first_key" = B{"John Smith", 28},
    "second_key" = B{"Jane Doe", 34}
};
And now I can serialize any instance of A to json by just doing this:
Meta<A>::serialize(a);
This will return this JSON:
{
    "first_key" = {
        name = "John Smith",
        age = 28
     },
     "second_key" = {
        name = "Jane Doe",
        age = 34
     }
}
Here are two things to note:
unordered_map is serialized into a JSON map. Each value of the map is serialized by using Meta<B>::serialize (that's where recursion comes in!)
For defined types (int, float, bool, string, etc.) there are Meta<> specializations, so recursion stops there.

Same stuff works for ImGui almost the same, but the neat thing is that I can map each member directly to a GUI control by passing a pointer to the member into ImGui function. There's no copies, no temporary objects. The C++ object is modified directly. And this is really great and awesome!

And here are some questions I have...
1) How should I handle error handling? Should I throw exceptions? I want to be able to write error messages like this: "Error: Animation::name should be string, but int was passed" if JSON is incorrect. Should serialize/deserialize throw exceptions like this? (Right now I only return true if deserialization was successful and false otherwise... which is not great, of course!), or maybe there are other ways to handle errors... (Silently ignoring them is not the way!)
2) Is there any reliable way to create function for template class which will be called on template class instantiation?  Is such thing possible? How can I achieve it and be sure that static members are initialized at the point the function is called?
For example, I write this function:
void Animation::registerClass(MetaBuilder<Animation>& builder)
and I want it to be called automatically by Meta<Animation> class. I want this to be called automatically because of Meta<T> instantiation:
Meta<T>::init() {
    T::registerClass(MetaBuilder<T>());
}

P.S. All meta stuff code will be open sourced soon! I'll also show how to use ImGui with it a bit later. :)
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re:creation - a top down action adventure about undeads
« Reply #589 on: May 14, 2016, 11:34:43 pm »
Jesper Juhl, I was talking about forum threads, not other threads, so it's alright.
Whoops.  ::)  ;D

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re:creation - a top down action adventure about undeads
« Reply #590 on: May 15, 2016, 09:34:43 am »
Quote
Is there any reliable way to create function for template class which will be called on template class instantiation?  Is such thing possible?
No, I don't think so. Manual registration of each class will be necessary.
Laurent Gomila - SFML developer

dabbertorres

  • Hero Member
  • *****
  • Posts: 505
    • View Profile
    • website/blog
Re:creation - a top down action adventure about undeads
« Reply #591 on: May 15, 2016, 10:32:13 am »
1) How should I handle error handling? Should I throw exceptions? I want to be able to write error messages like this: "Error: Animation::name should be string, but int was passed" if JSON is incorrect. Should serialize/deserialize throw exceptions like this? (Right now I only return true if deserialization was successful and false otherwise... which is not great, of course!), or maybe there are other ways to handle errors... (Silently ignoring them is not the way!)

I think this is a good use of exceptions.
I usually throw the exception with the error message, catch the exceptions in the caller, and then log the message (plus anything else I find necessary) from there.

2) Is there any reliable way to create function for template class which will be called on template class instantiation?  Is such thing possible? How can I achieve it and be sure that static members are initialized at the point the function is called?
For example, I write this function:
void Animation::registerClass(MetaBuilder<Animation>& builder)
and I want it to be called automatically by Meta<Animation> class. I want this to be called automatically because of Meta<T> instantiation:
Meta<T>::init() {
    T::registerClass(MetaBuilder<T>());
}

If I am understanding what you want to do correctly, I think you could add a static MetaBuilder<T> member to Meta<T>. And then, in the constructor of MetaBuilder<T>, call T::registerClass(*this)

template<typename T>
class MetaBuilder
{
        public:
                MetaBuilder()
                {
                        T::registerClass(*this);
                }
};

template<typename T>
class Meta
{
        static MetaBuilder<T> metaBuilder;
};

 

Also, it looks like this would be more standard C++. It looks like you're passing a temporary to a non-const reference parameter, which is a VC++ extension.

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #592 on: May 15, 2016, 12:43:57 pm »
Quote
Is there any reliable way to create function for template class which will be called on template class instantiation?  Is such thing possible?
No, I don't think so. Manual registration of each class will be necessary.
Yeah, it'll still be manual, but I don't want to call Meta<T>::init() somewhere in code.

I think this is a good use of exceptions.
I usually throw the exception with the error message, catch the exceptions in the caller, and then log the message (plus anything else I find necessary) from there.
Yeah, I'm probably going to use them, thanks!
Your second point about static MetaBuilder variable in interesting, but it doesn't work, because MetaBuilder ctor won't be called automatically (I don't really know why, I've tried this).

There's one method which works, but it looks really dangerous, ha-ha. (Not sure if it will work on every compiler...)
I put these two lines in MetaBuilder<T>::add(...):
(void)Meta<T>::properties; // unordered_map which stores class member pointers
(void)Meta<T>::initialized; // bool which calls Meta<T>::init, see below

And here's a line which is tricky:
bool Meta<T>::initialized = Meta<T>::init();

And here's Meta<T>::init():
MetaBuilder<T> builder;
T::registerClass(builder);

And here's how it all works.
When I write T::registerClass function for particular class, I call MetaBuilder::add<T> methods in it which causes code for MetaBuilder::add<T> to generate. Then I "touch" Meta<T>::properties so that this member is initialized before Meta<T>::initialized. The bool is "touched" too, so it's initialized by calling Meta<T>::init() which calls T::registerClass.

The most dangerous and (probably) unstable part is order of static initialization. If Meta<T>::initialized is initialized before Meta<T>::properties, then the code won't work. Yeah, pretty crazy stuff. :D

(At this point I'm really starting to question this approach which will probably lead me to abandon the idea of calling Meta<T>::init automatically)
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

dabbertorres

  • Hero Member
  • *****
  • Posts: 505
    • View Profile
    • website/blog
Re:creation - a top down action adventure about undeads
« Reply #593 on: May 16, 2016, 04:50:48 am »
Ah yes, I forgot about a few of those odd static initialization rules, especially with templates.

I got something working though:
(click to show/hide)

Basically, two things are required: an out-of-class provision of Meta<T>::metaBuilder, and a (non-) use of the member. (Above, in Meta<T>::doWork()). Basically, for the compiler/runtime to decide for it to be statically initialized as desired, it must be directly accessed. Plain-Old-Data types don't have this restriction. Kinda weird. But it works, and it's not even that much work.

tl;dr: The above code will have T::registerClass() called for any class used as Meta<T>'s template parameter.

EDIT: Test::registered was just for testing, it wouldn't ever actually be needed, as it will always be true.

So, we came to quite similar conclusions. The constructor being called when desired is the only difference!
« Last Edit: May 16, 2016, 04:55:38 am by dabbertorres »

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #594 on: May 16, 2016, 09:04:27 am »
dabbertorres, you know, at this point it's easier to call Meta<T>::init() directly because you have to call Meta<T>::doStuff which does almost the same thing, but a lot harder.
My solution is more complicated as it calls init function just by  instantiating MetaBuilder<T> and calling some of it's functions while registering the class, so no additional calls needed.
For example, when I write this:
void Animation::registerClass(MetaBuilder<Animation>& builder)
{
    builder.add("name", &Animation::name);
    ...
}
 
I call MetaBuilder<Animation>::add. Just instantiation of this method triggers instantiation of Meta<T> and Meta<T>::init is called through some tricks which I explained earlier.

I'm not sure how reliable that method is, though. :D
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

dabbertorres

  • Hero Member
  • *****
  • Posts: 505
    • View Profile
    • website/blog
Re:creation - a top down action adventure about undeads
« Reply #595 on: May 16, 2016, 05:05:09 pm »
Hehe, I don't blame you! At least it's fun to experiment with different solutions. :D

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #596 on: May 26, 2016, 12:15:29 am »
Right now I'm working on some art and I'm making very neat in-game animation editor (screenshots and gifs coming soon, there'll be lots of features there).

I recently open sourced serialization/deserialization stuff which I use in my game.
Here it is: https://github.com/EliasD/MetaStuff
Other system was used for past several months, but I came up with this system recently and I find it a lot more flexible and easier to work with. Check it out, maybe it'll become useful for you. :)
(Maybe I'll even make an example which will show how it's possible to use ImGui with it to modify members of C++ objects).
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re:creation - a top down action adventure about undeads
« Reply #597 on: May 26, 2016, 09:03:22 am »
Now that your code is public I can give you various minor comments about it :P

It seems like JsonCast.h has found a way into the "include" folder when it should only be in "example" ;D

Also, you shouldn't use #pragma once alone, even if it's supported by all the common compilers that are able to compile your code.

You should include C++-style headers (<cassert> instead of <assert.h>).

You should enclose your functions and classes into a namespace. "member" is too common to be left in the global scope.

I like the C++14 implementation, it's really small and yet does the job perfectly. The 100% generic approach has some limitations though (ie. how would I get/store/use a single specific MemberPtr?), but you can easily expand this code if your requirements evolve.
Laurent Gomila - SFML developer

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re:creation - a top down action adventure about undeads
« Reply #598 on: May 26, 2016, 10:04:55 am »
it is interesting, i haven't seen alike it before, but what took my attention most is your meta function "for_each_arg". i'm afraid, it is not going to do what it is supposed to do, for some reasons the args order is not guaranteed. probably it will fail if args... is empty. you may take a look to this http://stackoverflow.com/questions/28110699/what-does-this-variadic-template-code-do?answertab=votes#tab-top for more details.
« Last Edit: May 26, 2016, 10:51:01 am by MORTAL »

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re:creation - a top down action adventure about undeads
« Reply #599 on: May 27, 2016, 12:05:30 am »
Now that your code is public I can give you various minor comments about it :P
Thank you for your comments! That's exactly what I wanted, especially coming from awesome programmers like you. :)

It seems like JsonCast.h has found a way into the "include" folder when it should only be in "example" ;D
Fixed!

Also, you shouldn't use #pragma once alone, even if it's supported by all the common compilers that are able to compile your code.
Should I use both pragma once and include guards?

You should include C++-style headers (<cassert> instead of <assert.h>).
Thanks, forgot about that. Fixed.

You should enclose your functions and classes into a namespace. "member" is too common to be left in the global scope.
I have no idea for the namespace yet, any suggestions? :)

I like the C++14 implementation, it's really small and yet does the job perfectly. The 100% generic approach has some limitations though (ie. how would I get/store/use a single specific MemberPtr?), but you can easily expand this code if your requirements evolve.
Thank you! Yeah, getting/storring specific MemberPtr is not yet possible, but doing some stuff for a single member is possible, like this:
for_tuple([](const auto& memberPtr) {
    if (memberPtr.getName() == "someVar") {
        // do something for this particular member
    }, Meta::getMembers<SomeClass>());
Too bad it works in O(n), though it should be much problem. I wish there was a way to store members not in tuple, but in some unoderder_map like structure, this would make things much easier, but this structure has to be heterogeneous!
And I will surely add some stuff (I've added some neat things today, see below)

it is interesting, i haven't seen alike it before, but what took my attention most is your meta function "for_each_arg". i'm afraid, it is not going to do what it is supposed to do, for some reasons the args order is not guaranteed. probably it will fail if args... is empty. you may take a look to this http://stackoverflow.com/questions/28110699/what-does-this-variadic-template-code-do?answertab=votes#tab-top for more details.
Thanks for you comment. The order is not guaranteed, yep, but VS, GCC and Clang to stuff in order. I realize that this is not guaranteed but at least there's some order for some compilers! :D
I'll take a look at this later, though. For now you can just think of std::tuple as if it was std::unordered_map with member names as keys. :)
As for empty Args... see my update below. I've added and overload for empty tuple which takes empty tuple and just does nothing with it. This fixes the problem (unless the user calls for_each_arg... which is not needed! :D)

Okay, and now for some new stuff...
(I hope people won't mind me discussing all this stuff in this thread, I wish I could start another thread about this lib, but it's not an SFML Project, so...)

New stuff
I've changed getMembers from being a class member function to Meta struct templated function.
I could have made it a free function, but it's hard to make a template specialization a friend, it's much easier to write "friend class Meta" in classes you want to serialize (it's not required most of the time, though)

Template specialization looks like this:
#include <Meta.h>
template <>
inline auto& Meta::getMembers<Person>()
{
    static auto memberPtrs = std::make_tuple(
        member("someMember", &Class::someMember),
        ...);
    return memberPtrs;
}
 

This template specialization should be included in header, because compiler can't deduce return type if you then try to use this specialization somewhere else.
I was worried that there would be some problems with mixing static with inline (after all Meta::getMembers<T> is static! And inline! And contains static variable! What if it's copied between translation units?)
Worry not, here's a quote which saved my project! :D
Quote
"[..] An inline function with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in an extern inline function is the same object in different translation units." - [dcl.fct.spec]/4
Cool, so the  memberPtrs tuple is created ONCE for each registered type. That's exactly what I want. And here's another cool thing, template function implementation looks like this:

template <typename T>
inline auto& Meta::getMembers()
{
    static std::tuple<> emptyTuple;
    return emptyTuple;
}
 

This means that getMembers<Class>() returns empty tuple for all unregistered classes! (Instead of producing compile error)
Not sure that this is expected behaviour, but I've added overload for forTuple which takes empty tuple. This means that if you do this:
orTuple(/* some lambda */, Meta::getMembers<SomeUnregisteredClass>());
then it'll do nothing.

Some questions
1) I'm thinking about renaming MemberPtr to just Member. Is this reasonable? After all some MemberPtr's may contain only getter and setter function pointer... :D
2) Is there some good namespace which I can use for my project? I have no idea for the name yet. Simple "meta" would be nice, but I feel that it's too generic.
3) What should library do in exceptional cases? (he-he)
Should it assert? Should it throw an exception? Here's and example, I want to add a function which would return non-const reference to member. For example:
T& MemberPtr::getNonConstRef(Class& obj);
But this is only possible if MemberPtr contains pointer to member or pointer to non-const getter.
So, what should I do in such case? (there are many cases like that)
« Last Edit: May 27, 2016, 12:10:57 am by Elias Daler »
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler