SFML community forums

General => SFML projects => Topic started by: Nexus on March 19, 2012, 06:16:23 pm

Title: Aurora
Post by: Nexus on March 19, 2012, 06:16:23 pm
Aurora

Although this project isn't directly related to SFML, I thought some people may find it interesting. Formerly a part of Thor, Aurora is now an autonomous library that provides various C++ functionality which can be useful in different situations.

I know this sounds extremely abstract, so here is an example of a quite intelligent smart pointer:
#include <Aurora/SmartPtr/CopiedPtr.hpp>

namespace sf { class Drawable; } // Forward declaration is enough

struct GameObject
{
        aur::CopiedPtr<sf::Drawable> drawable;
};

#include <SFML/Graphics.hpp>

int main()
{
        GameObject a, b, c;
        a.drawable.reset(new sf::RectangleShape);
        b.drawable.reset(new sf::Sprite); // different derivates
        c = a; // performs a deep copy
} // deletes everything correctly
sf::Drawable is an abstract base class, but we can wrap it into a smart pointer. Like this, GameObject has normal value semantics, i.e. we can copy it as usual, while the derived type (sf::RectangleShape) is copied correctly through the hierarchy. This without a single copy constructor, assignment operator or a virtual clone function.

Another example are the dispatchers (also known as a way to emulate multimethods in C++). Their task is to choose the correct overload of a set of functions, of which the arguments are only known at runtime. This allows you to work with abstract base classes (like Object in the example), while interaction between two objects can still be implemented with the full type information. No need for manual case differentiations.
#include <Aurora/Dispatch.hpp>
#include <iostream>

// Example class hierarchy
class Object { public: virtual ~Object() {} };
class Asteroid : public Object {};
class Ship     : public Object {};

// Functions for collision with different argument types
void collision(Asteroid*, Asteroid*)    { std::cout << "Asteroid-Asteroid\n"; }
void collision(Asteroid*, Ship*)        { std::cout << "Asteroid-Ship\n";     }
void collision(Ship*,     Ship*)        { std::cout << "Ship-Ship\n";         }

int main()
{
    // Create dispatcher and register functions
    aur::DoubleDispatcher<Object*> dispatcher;
    dispatcher.add<Asteroid, Asteroid>(&collision);
    dispatcher.add<Asteroid, Ship>    (&collision);
    dispatcher.add<Ship,     Ship>    (&collision);

    // Base class pointers (no static type information about derived classes)
    Object* a = new Asteroid;
    Object* s = new Ship;

    // Invoke functions, let dispatcher choose the correct overload
    dispatcher.call(a, s); // Output: "Asteroid-Ship"
    dispatcher.call(a, a); // Output: "Asteroid-Asteroid"
    dispatcher.call(s, s); // Output: "Ship-Ship"
   
    delete a;
    delete s;
}

Aurora uses the zlib/libpng license. Since it is a header-only library, it requires no build process and can thus be very easily be used in your projects.

Links
Aurora project homepage (http://www.bromeon.ch/libraries/aurora/index.html)
GitHub page (https://github.com/Bromeon/Aurora)
Title: Aurora
Post by: Silvah on March 19, 2012, 06:59:52 pm
Could you change the namespace name? It's not like typing three characters less is going to save the world, and if it is, one can always use an alias. This library has nothing to do with AUR (http://aur.archlinux.org/), but looking at the namespace one could think otherwise.
Title: Aurora
Post by: thePyro_13 on March 20, 2012, 05:47:56 am
Should've called it Odin or something, to tie into Thor. :D

I wonder If it'd be possible to modify the dispatcher to work with run time defined object times(scripted entities)?
Title: Aurora
Post by: Nexus on March 20, 2012, 11:45:50 am
Quote from: "Silvah"
This library has nothing to do with AUR (http://aur.archlinux.org/), but looking at the namespace one could think otherwise.
Thanks for the hint, I wasn't aware of the ambiguity. But is it really an issue? Because 6 characters is really a bit long for something you always type, I mean Laurent even shortened sfml to sf ;)

Quote from: "thePyro_13"
Should've called it Odin or something, to tie into Thor.
Theoretically, I could still change the name, this would also solve the namespace problem :P But apart from that, "Aurora" actually pleases me (and since it's not directly related to Thor, it needn't be another Germanic god, Roman goddess is also fine ;))

Quote from: "thePyro_13"
I wonder If it'd be possible to modify the dispatcher to work with run time defined object times(scripted entities)?
Could you make an example? I think it might require bigger refactoring if the objects aren't represented by C++ classes...
Title: Aurora
Post by: Silvah on March 20, 2012, 04:49:01 pm
Quote from: "Nexus"
Because 6 characters is really a bit long for something you always type, I mean Laurent even shortened sfml to sf ;)
boost::filesystem? As I said, one can always use an alias (e.g. "namespace foo = long_namespace_name"). Not to mention that modern IDEs have a nice feature called autocompletion ;)
Title: Aurora
Post by: thePyro_13 on March 21, 2012, 02:57:43 am
Quote from: "Nexus"
Quote from: "thePyro_13"
I wonder If it'd be possible to modify the dispatcher to work with run time defined object times(scripted entities)?
Could you make an example? I think it might require bigger refactoring if the objects aren't represented by C++ classes...
The objects would only have a generic C++ type. They differentiate themselves using identifiers provided from the script.

After actually looking at how dispatch works, it probably wouldn't do to try and shoehorn it for this situation. I'll have to make something more specific IMO.
Title: Aurora
Post by: Nexus on March 21, 2012, 11:31:13 am
Couldn't this be achieved relatively easy with a std::map<ObjectIdentifier, std::function<X>>?

My dispatchers essentially do the same thing, where ObjectIdentifier is the std::type_info of the object. In this respect, it might even be possible to generalize the dispatchers.
Title: Aurora
Post by: Orwel on March 21, 2012, 08:32:04 pm
Somebody have test example. Dont work for me  :cry:
Title: Re: Aurora
Post by: Nexus on March 25, 2012, 09:51:18 am
Sorry, the Dispatcher example was incorrect. I reduced it from a bigger example and introduced some mistakes :-\

Should be okay now. The smart pointer example was already correct before.
Title: Re: Aurora
Post by: Orwel on April 05, 2012, 11:19:57 pm
Thank, now this example is perfect. You can add this line to show symetry is possible(cf doc):
Code: [Select]
dispatcher.call(s, a); // Output: "Asteroid-Ship"
I had tried to read code to understand how run dispatcher, but i didn't understand  :-[

You can make a short and simple explanation, please  ;D
Title: Re: Aurora
Post by: Tank on April 06, 2012, 01:28:17 pm
I especially like the dispatcher. But how's the smart pointer any different from the generic solutions in C++11 and Boost?
Title: Re: Aurora
Post by: MorleyDev on April 06, 2012, 05:20:04 pm
I especially like the dispatcher. But how's the smart pointer any different from the generic solutions in C++11 and Boost?

As I understand it, it copies the value into a new heap allocated object. shared_ptr copies the reference and increments a counter, unique_ptr can't copy but can move the reference.
Title: Re: Aurora
Post by: Silvah on April 06, 2012, 06:38:08 pm
Time to try again, I guess.

Quote from: Nexus
Because 6 characters is really a bit long for something you always type, I mean Laurent even shortened sfml to sf ;)
boost::filesystem? As I said, one can always use an alias (e.g. "namespace foo = long_namespace_name"). Not to mention that modern IDEs have a nice feature called autocompletion ;)
Title: Re: Aurora
Post by: Nexus on April 06, 2012, 06:59:58 pm
I had tried to read code to understand how run dispatcher, but i didn't understand  :-[
Have you already read the documentation (http://www.bromeon.ch/libraries/aurora/v1.0/doc/classaur_1_1_double_dispatcher.html)? What parts exactly don't you understand?

But how's the smart pointer any different from the generic solutions in C++11 and Boost?
The C++11 standard library and Boost don't provide any smart pointers with deep copy semantics at all. That is, you cannot copy the referenced object in general. The aur::CopiedPtr<T> class template is a wrapper around raw pointers with the intention to treat them like normal objects with value semantics. I've just uploaded a draft of the tutorial (http://www.bromeon.ch/libraries/aurora/v1.0/tutorial-smartptr.html), there I explain some of the details in case you're interested.

Time to try again, I guess.
I have acknowledged it, I'm still reflecting whether I should change the namespace name or not. I'd also appreciate feedback from other users :)
Title: Re: Aurora
Post by: Tank on April 08, 2012, 12:39:45 pm
Yeah, but one can always do that via

std::shared_ptr<Foo> copy( new Foo( *original ) );

(works for other smart pointers in a similar/an equal way, of course) Even easier would be a free function like "clone()" that works with every smart pointer.

Imho it's getting too specialized when doing a separate type for copy semantics for heap-allocated objects. And copying has, in my understanding, not really something to do with the type of a pointer. Could be personal preference though. :)
Title: Re: Aurora
Post by: Nexus on April 08, 2012, 01:11:36 pm
Yeah, but one can always do that via
std::shared_ptr<Foo> copy( new Foo( *original ) );
No. Foo may be polymorphic, so you slice the object. Or there might even be no copy constructor, but for example a virtual clone() function or a custom allocator.

Imho it's getting too specialized when doing a separate type for copy semantics for heap-allocated objects.
Why? There are smart pointers dedicated to unique and shared ownership, why not copied ownership? This is something that's widely overlooked in my opinion, many people still make their lives more complicated than necessary with low-level and boilerplate code.

And copying has, in my understanding, not really something to do with the type of a pointer.
Yes, it does, as well as destruction can differ at different types. Although we normally use copy constructor and destructor, there are justified use cases for custom deleters (implemented in std::unique_ptr and std::shared_ptr) as well as custom cloners.

For example, we have an interface BackEnd and a derived class ConcreteBackEnd. Let's implement the Pimpl idiom using std::unique_ptr (by the way, we're injuring the Rule of The Big Three, because defining a destructor is pointless):
struct BackEnd
{
        virtual ~BackEnd() {}
        virtual BackEnd* clone() const = 0;
};

struct ConcreteBackEnd : BackEnd
{
        virtual BackEnd* clone() const
        {
                return new ConcreteBackEnd(*this);
        }
};

class FrontEnd
{
        private:
                std::unique_ptr<BackEnd> ptr;

        public:
                ...

                FrontEnd(const FrontEnd& origin)
                : ptr(origin.ptr->clone())
                {
                }

                FrontEnd& operator= (const FrontEnd& origin)
                {
                        ptr.reset(origin.ptr->clone());
                }
};

Now the same functionality using Aurora's CopiedPtr:
struct BackEnd
{
        virtual ~BackEnd() {}
};

struct ConcreteBackEnd : BackEnd
{
};

class FrontEnd
{
        private:
                aur::CopiedPtr<BackEnd> ptr;

        ...
};

You see, there's no need for the clone() function, neither for copy constructor nor assignment operator. Code is shorter and less error prone (for example, one can easily derive from ConcreteBackEnd without overriding clone(), leading to slicing bugs). And it gets even more complicated if null pointers are an allowed state, in this case one always needs to check for validity before copying.
Title: Re: Aurora
Post by: Tank on April 08, 2012, 07:05:55 pm
Legit reasons, thanks for pointing them out. To be honest I didn't thought deep enough. :)
Title: Re: Aurora
Post by: Nexus on September 16, 2012, 10:16:57 pm
Okay, meanwhile the layer of dust is thick enough... :D

In fact, I have not worked a lot on Aurora in the last time, as there was no big demand for new features. There have been smaller modifications, which include:

Foreach loop
The limited C++11 compiler support of Visual C++ forces me to write workarounds for range-based for. AURORA_FOREACH behaves almost like BOOST_FOREACH, however its implementation is far less complex and doesn't require tons of headers, thanks to C++11 language features.
std::vector<int> v;
AURORA_FOREACH(int& i, v)
    ++i;

Factory functions for std::unique_ptr and aurora::CopiedPtr
Because the C++ standard commitee has forgotten std::make_unique() analogous to std::make_shared(), I provide it in Aurora. I also wrote aurora::makeCopied(). These functions should make the construction of smart pointers more idiomatic and hide the new operator from application code.
auto window = aurora::makeUnique<sf::RenderWindow>(videoMode, "SFML Window");
// instead of
std::unique_ptr<sf::RenderWindow> window(new sf::RenderWindow(videoMode, "SFML Window"));
Title: Re: Aurora
Post by: mateandmetal on September 21, 2012, 08:04:10 am
The limited C++11 compiler support of Visual C++ forces me to write...

So, instead of using another compiler that better suits your needs, you just write your own C++11 implementation  ???

I think you are setting your own limits
Title: Re: Aurora
Post by: eXpl0it3r on September 21, 2012, 08:18:42 am
So, instead of using another compiler that better suits your needs, you just write your own C++11 implementation  ???

I think you are setting your own limits
Well the library is not developed only for his purpose and he probably doesn't like to force people into using a specific compiler.
Keeping the compabiliy will limit the ability of C++11 features but keeps the possibility of using MSVC. It's not the nicest thing, but it maybe better than not supporting MSVC at all.

For the one game I'm working on, we're currently moving to MinGW 4.7.0, because of the lack of C++11 features, but we don't need to support MSVC because it's only the dev team that has to work with the code... ;)
Title: Re: Aurora
Post by: Nexus on September 21, 2012, 01:28:23 pm
So, instead of using another compiler that better suits your needs, you just write your own C++11 implementation  ???
Well, it's not that easy. You cannot compare library development 1:1 to non-portable end-user projects.

I don't want to restrict people to a single compiler, even if this would simplify a lot for me. And I can't just ignore Visual C++, it is still widely used, and comes with one of the best IDEs for C++. Already basic C++11 excludes a vast range of possible compilers, but at least I consider this justified by the language features.

Besides, this is not the first time I have to write compiler-specific workarounds. The TR1 implementation of the g++ standard library contained many bugs, as a consequence I had to rewrite standard random distributions in Thor 1.1. Very recently, I was forced to abandon lambda expressions in a special situation because of a Clang compiler bug.
Title: [Aurora] Enter Ranges
Post by: Nexus on October 26, 2012, 06:41:06 pm
Aurora contains now a new module: Range.

This is another library feature I have been missing a lot recently. Ranges are an abstraction of C++ iterators, a range object in its most basic case is a (begin,end) iterator pair. It represents a view to a sequence, and as such, it can be used in algorithms where most functionality of the original container is not important.

Although there are libraries like Boost.Range, there exist hardly useful implementations for type-erased ranges, which are provided by Aurora. Type-erasure refers to the underlying iterator type, which is abstracted away -- only the element type is important. This has the following advantages:
Aurora provides the class template Range<T, C>. The first template parameter is the element type. The second is the traversal category, which determines what operations can be invoked on the range.

For example, aurora::Range<int, aurora::Traversal::Bidirectional> models a bidirectional range referring to int elements. Often, C++11 type inference allows to omit the exact type, as in the following example:
#include <Aurora/Range.hpp>

#include <vector>
#include <list>
#include <iostream>

int main()
{
        // Create random access container
        std::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);

        // Create bidirectional container
        std::list<int> l;
        l.push_back(4);
        l.push_back(5);

        // Create ranges to the containers
        auto first = aurora::makeRange(v);
        auto second = aurora::makeRange(l);

        // Concatenate two ranges
        auto all = aurora::chain(first, second);

        // Output all elements: 1 2 3 4 5
        for (; !all.empty(); all.popFront())
                std::cout << all.front() << ' ';
}
Title: Re: Aurora
Post by: Lo-X on October 26, 2012, 07:08:21 pm
Even if I do not see immediately the use I can do, I like the idea (I dindn't know boost::range). :)
Title: Re: Aurora
Post by: FRex on October 26, 2012, 09:18:12 pm
Might want to change the
Quote
The library is split into three modules:
line on Thor page after this addition. ;)
I assume thor and aurora are c++11 only, no?
Title: Re: Aurora
Post by: Lo-X on October 26, 2012, 10:15:57 pm
Might want to change the
Quote
The library is split into three modules:
line on Thor page after this addition. ;)
I assume thor and aurora are c++11 only, no?

I think the auto just above is an affirmative answer to that question =)
Title: Re: Aurora
Post by: FRex on October 26, 2012, 10:19:15 pm
So I've thought. Too bad.
Title: [Aurora] More flexible dispatchers
Post by: Nexus on November 28, 2013, 08:30:19 pm
Time passes...

I reworked the dynamic dispatchers. aurora::SingleDispatcher and aurora::DoubleDispatcher are now not only able to work with polymorphic class hierarchies, but virtually any object relations. This change was performed with designs such as component-based systems in mind. The requirement is a type identifier which is then used as a key to lookup functions in the map -- using a traits class, this behavior can be customized.

Implementation-wise, I am now using std::function (instead of a self-built class) to store the functions and std::unordered_map (instead of a sorted std::vector) to map keys to functions.

Here comes an example what the new DoubleDispatcher can do; for SingleDispatcher it's basically the same.
#include <Aurora/Dispatch/DoubleDispatcher.hpp>
#include <iostream>

enum ID { A, S };
struct Entity { ID id; };

// Customization for own type
struct Traits : aurora::DispatchTraits<ID>
{
        static ID keyFromBase(Entity& e)
        {
                return e.id;
        }
};

// Overloads
void collisionAA(Entity&, Entity&) { std::cout << "Asteroid-Asteroid\n"; }
void collisionAS(Entity&, Entity&) { std::cout << "Asteroid-Ship\n"; }
void collisionSS(Entity&, Entity&) { std::cout << "Ship-Ship\n"; }

int main()
{
        // Register functions
        aurora::DoubleDispatcher<Entity&, void, Traits> disp;
        disp.bind(A, A, &collisionAA);
        disp.bind(A, S, &collisionAS);
        disp.bind(S, S, &collisionSS);

        // Dispatch on argument
        Entity s = { S }, a = { A };
        disp.call(a, a); // Output: Asteroid-Asteroid
        disp.call(a, s); // Output: Asteroid-Ship
        disp.call(s, a); // Output: Asteroid-Ship
        disp.call(s, s); // Output: Ship-Ship
}

It is still possible to use classical base-derived hierarchies (this is actually the default). The syntax is slightly more verbose than before, but it's possible to simplify it with user-defined functions, should this be a concern. As you see, you can also use pointers instead of references, just like before.
#include <Aurora/Dispatch/DoubleDispatcher.hpp>
#include <iostream>

struct Base { virtual ~Base() {} };
struct Asteroid : Base {};
struct Ship : Base {};

void collisionAA(Asteroid*, Asteroid*)    { std::cout << "Asteroid-Asteroid\n"; }
void collisionAS(Asteroid*, Ship*)        { std::cout << "Asteroid-Ship\n"; }
void collisionSS(Ship*, Ship*)                { std::cout << "Ship-Ship\n"; }

int main()
{
        using aurora::Type;

        aurora::DoubleDispatcher<Base*> disp;
        disp.bind(Type<Asteroid>(), Type<Asteroid>(), &collisionAA);
        disp.bind(Type<Asteroid>(), Type<Ship>(),     &collisionAS);
        disp.bind(Type<Ship>(),     Type<Ship>(),     &collisionSS);

        // Dispatch on argument
        Asteroid a;
        Ship s;
        Base* pa = &a;
        Base* pb = &s;
        disp.call(pa, pa); // Output: Asteroid-Asteroid
        disp.call(pa, pb); // Output: Asteroid-Ship
        disp.call(pb, pa); // Output: Asteroid-Ship
        disp.call(pb, pb); // Output: Ship-Ship
};

A feature that is not available anymore is automatic derived-to-base conversions in the latter example. The backend is still in Aurora, but it's not used in the dispatchers. I might think about adding it again in the future, but at the moment it seems like nobody is using the dispatchers (or Aurora in general) anyway ;)
Title: Re: Aurora
Post by: Nexus on January 15, 2014, 12:42:38 am
Time to announce some unconventional features again! 8)

Named tuples

The C++11 standard library provides the std::tuple class template, which is able to store multiple types. It provides many handy features, such as member-wise equality comparison and lexicographical ordering, making it suitable to be used in STL containers. However, standard pairs and tuples have a big disadvantage: accessing their elements is not expressive at all.
std::tuple<int, double> tuple(1, 3.42);
int i = std::get<0>(tuple);

Code like that is not only hard to read, but also error-prone: You have to know the order of elements by heart, using a wrong index may lead to silent bugs. Furthermore, you cannot associate any meaning to the tuple member; it's unclear what the int stands for.

A common alternative is to use simple struct types, where it's possible to give names to the member variables. However, this approach suffers again several limitations: You have to define all the functionality you need yourself. The compiler-generated default constructor may leave some members uninitialized, and a custom constructor is still required in many cases, even if it just initializes member by member. operator== and operator< have to be hand-written, and the definition of the latter is often non-trivial.

Aurora introduces a named tuple type that combines the functionality of std::tuple with the expressive and intuitive usage of user-defined structs. Named tuples are very effective at avoiding boilerplate code. A named tuple is defined by using a macro:
AURORA_NAMED_TUPLE(UnitData,
(
    (sf::Vector2f, position),
    (float,        rotation),
    (int,          hitpoints)
))

AURORA_NAMED_TUPLE(Index2D,
(
        (std::size_t, x),
        (std::size_t, y)
))

The first parameter is the type name, the second is a preprocessor sequence of type-value pairs, declaring each member variable. Extended functionality can easily be introduced by using the macro AURORA_NAMED_TUPLE_EXT. For example, the following code defines an operator== and a default constructor. At the moment, the extensions EQUAL, LESS, DEFAULT_CTOR and HASHER are available.
AURORA_NAMED_TUPLE_EXT(UnitData,
(
    (sf::Vector2f, position),
    (float,        rotation),
    (int,          hitpoints)
),
(AURORA_NT_EQUAL, AURORA_NT_DEFAULT_CTOR))

Every named tuple defines a constructor taking a value for each member, a default constructors is defined as shown above. The type can now be used as follows:
UnitData d(sf::Vector2f(200, 300), 270.f, 20);

UnitData e;
e.position = sf::Vector2f(200, 300);
e.rotation = 270.f;
e.hitpoints = 20;

assert(d == e);

Named tuples are convertible to std::tuple by using the toStdTuple() member function.
std::tuple<sf::Vector2f, float, int> t = d.toStdTuple();
Title: Re: Aurora
Post by: Lolilolight on January 15, 2014, 09:37:48 am
Wow nice, I was just checking about a structure with can initialize all elements of a tuple (who's using a variadic template) with defaults constructors.

To avoid to have a crash if I forget to initialize the tuple and then to pass a tuple with 0 arguments at a function pointer who expect to have one or more arguments.

std::tuple are not very practice because :

-You have to do an helper to access at every element of a variadic template tuple.
-If you get an element in a tuple who's empty, it crash.
-And I don't even know how to replace the elements in a tuple (like we do it with a list...), so, I replaced the tuple entierly, or, I would like to use a place holder system.
-How can I pass references to a tuple, I tried to use std::ref but it didn't work. (Should I use std::move ?)

So my questions are :

Can we pass references to your named tuple ?
Can we change an element of your named tuple ?
And does it crash if I don't pass an element to your tuple (even if the tuple expects to have one or more elements) ?
-Does it work with variadic template ?
Title: Re: Aurora
Post by: Nexus on January 15, 2014, 02:16:53 pm
Aurora's named tuples are just structs with the specified members and extensions. They work as if you declared the struct directly, but by using the macro you don't need all the boilerplate code. Named tuples are not implemented similarly to std::tuple, there are no templates whatsoever.

Can we pass references to your named tuple ?
No. Why would you use references? They're very unflexible inside containers, for example they prohibit assignment. Use pointers instead.

Can we change an element of your named tuple ?
You can change the value, but of course not the type.

And does it crash if I don't pass an element to your tuple (even if the tuple expects to have one or more elements) ?
No, nothing crashes. Everything is type-safe at compile time. You cannot leave a value out in the constructor; either you pass values for all members or for none (when a default constructor is available).

-Does it work with variadic template ?
There are no templates involved, thus no. But you can pass a list of members anyway, see the last post.
Title: Re: Aurora
Post by: Lolilolight on January 15, 2014, 04:51:13 pm
Ok so I'll use pointers.

Erf, the problem is that I don't know the types and the number of types in advance so I need a variadic template. (My class can accepts any kind of functions with a variable argument list, and have a tuple to store the values of the arguments of the function)

And the signal use this object to store any kind of signal connected to any kind of slot and call it later but the problem is I've to pass the values of the arguments when I want to connect the slot at the signal, beacuse, if the template list is empty it crashes :
template <typename S, typename... A> static void connect (const std::string &key, S slot, A... args) {
         Signal *sign = new SignalWithoutReturn<S, A...> (slot, args...);
         std::map<std::string, Signal*>::iterator it = signals.find(key);
         if (it != signals.end())
            throw Erreur (20, "This id is already used!", 0);
         else
            signals.insert(std::make_pair(key, sign));
     }
 

Normal because if I don't fill the args variable in the connect function with values, it create a signal to a function with no arguments types, so, when I want to call it, it crash. ^^
I thought I could send a message at compilation time with your library but, it doesn't seems that we could.
The second thing I want to do is to pass placeholders at the signal who can be remplaced in the tuple by the real values of the arguments when I call the signal. Exactly like in this tutorial but with tuple, not with lists : http://accu.org/index.php/journals/1397 (http://accu.org/index.php/journals/1397)

The advantage of this system is that I've one type for any kind of signals (type erasure), not like std::bind where the type is unspecified so I cannot store an object with the std::bind type.
I don't really like the type auto and I don't really like the std::tuple class too. (tuple are difficult to manipulate and the auto type don't allow us to store objets so it's utility is very limited and I don't find it very well in a typed language like c++.)
The recursivity of variadic templates give me an headache too because we have to use the template specialisation and structures to do this instead of functions.
Title: [Aurora] Preprocessor metaprograms
Post by: Nexus on January 19, 2014, 11:24:12 am
Preprocessor metaprograms

Some of you might wonder how named tuples are implemented in Aurora. How can it be that the macro
AURORA_NAMED_TUPLE(MyTuple, ((int, i), (float, f)))
generates the following code?
struct MyTuple
{
    MyTuple(int i, float f) : i(i), f(f) {}

    int i;
    float f;
}

The answer leads to a dark corner of C and C++: Preprocessor metaprogramming. In general, macros have a bad reputation, and mostly they can be avoided by using other abstraction techniques such as templates. However, legitimate use cases remain; templates and even template metaprogramming don't cover every situation. For example, a previous post of mine (http://en.sfml-dev.org/forums/index.php?topic=7330.msg99223#msg99223) shows the drawbacks of std::tuple, namely its lack of expressiveness.

Most people are not aware that the C preprocessor is not only useful for simple text processing, but it has the capabilities of code generation -- before compile time, and within the language itself. As such, it is very powerful in specific situations where the only alternative would be manual and error-prone code repetition. These situations are rare, but when you encounter them, the preprocessor can prove invaluable.

Aurora provides a minimalistic preprocessor metaprogramming toolbox (http://www.bromeon.ch/libraries/aurora/v1.0/doc/_preprocessor_8hpp.html). While inspired from Boost.Preprocessor, some functionality is tweaked for specific needs, and some parts are simplified for the user (e.g. by the use of variadic macros). To show you how easy and powerful it is, let's define an enum which allows conversion to strings.
#include <Aurora/Meta/Preprocessor.hpp>

#define ENUMERATOR(value, index)    value,
#define TO_STRING(value, index)     case value: return AURORA_PP_STRINGIZE(value);

#define SMART_ENUM(Enum, sequence)              \
enum Enum                                       \
{                                               \
    AURORA_PP_FOREACH(ENUMERATOR, sequence)     \
};                                              \
                                                \
const char* toString(Enum e)                    \
{                                               \
    switch (e)                                  \
    {                                           \
        AURORA_PP_FOREACH(TO_STRING, sequence)  \
    }                                           \
}                                               \

Once we have defined the preprocessor metafunction SMART_ENUM, we can use it to generate code.
SMART_ENUM(Color, (Red, Blue, Green))
expands to:
enum Color
{
    Red,
    Blue,
    Green,
};

const char* toString(Color e)
{
    switch (e)
    {
        case Red: return "Red";
        case Blue: return "Blue";
        case Green: return "Green";
    }
}

And so, we can easily convert enumerators to strings.
int main()
{
    Color c = Green;
    const char* s = toString(c); // s is "Green"
}

Since the preprocessor doesn't allow iteration or recursion, metaprogramming helpers are implemented by means of manual repetition, which limits the number of processed elements. At the moment, sequences may not be longer than 5 elements, but this can be extended.

What do you think about named tuples and preprocessor metaprograms? Even if you don't need them immediately, can you imagine their utility? :)
Title: Re: Aurora
Post by: Lolilolight on January 19, 2014, 02:45:43 pm
Hum..., it depends on what the c++'ll reserve to us for later.

Personnaly I never used macro because of their reputation but, in the future why not, if the next c++ version doesn't offer us a more efficient way to manage tuples and exceptionnaly tuples with variadic templates to pass them to a function.

It'll be usefull to use a macro.
Title: Re: Aurora
Post by: Nexus on January 20, 2014, 10:50:22 pm
Other opinions on named tuples, dynamic dispatchers or preprocessor metaprograms? Or Aurora in general?

I'm totally aware that many of Aurora's features are very specific, and a lot of people don't need them. The idea is that when you ever need them, you'll be glad that you don't have to write, test, debug a lot of boring code -- especially for more complex things like dynamic dispatchers. I can imagine a lot of you already came in a situation where a similar technique has been necessary (see collision example (http://en.sfml-dev.org/forums/index.php?topic=7330.msg96007#msg96007)), how did you solve it then?

A notable exception to the rare use cases is aurora::CopiedPtr; I'm using it everywhere, and have found it extremely helpful so far. How do you copy polymorphic objects in case you need to?

In general, Aurora's philosophy is to be very lightweight, modular and easily integrable. You don't have to link anything and can directly start, and you can use only the parts you need. Other libraries with a similar application field (Boost) are the exact opposite. I'm really open for input regarding the library, especially if you don't use Aurora. Does the library look useful, but you have not had a situation where it would have fit; have you simply not known it well enough; or do you consider it a complete waste of time? :)
Title: Re: Aurora
Post by: MadMartin on January 21, 2014, 09:05:31 am
As almost every contribution you make, Nexus, this is C++-wise genius  ;)

My "problem" is that I haven't found a situation where Aurora could be helpful. For me, this is because you introduce very sophisticated programming techniques.

If you have the time and willingness, I'd absolutely love to see a blog of yours!
Although I consider myself an experienced user of C++, I always read your posts with great interest. For many of the features of modern C++ (C++11, RAII, Smart Pointers, etc.), your posts gave me new insight and/or more reasons to use them.
So, a blog with one or two nice articles about advanced C++ per month, written by you, would be something I even would pay for :)


But a simple set of nice and illustrating use cases for Aurora would be sufficient, to say the least! (Suggestion: Make them a little bit more elaborate than the examples on your page)
Title: Re: Aurora
Post by: Nexus on January 21, 2014, 04:54:44 pm
Thanks for the nice feedback! :)

Yes, bigger examples of Aurora's use cases might indeed be an idea. I kept them rather simple because I feared otherwise it would confuse people too much, and they would quickly lose the overview. But for example, are the code snippets in this thread too short? Or not "real-world" enough? For example, the codes about collision dispatchers and named tuples originate from an actual project of mine.

Yesterday, I also updated the Aurora project page (http://www.bromeon.ch/libraries/aurora/index.html) and added an explanation about the backgrounds and some small example codes. I guess you have already seen it.

Concerning the blog, I already thought about that, too, since I'm writing (and repeating) a lot of such topics in C++ forums. I probably couldn't guarantee regular updates, so it would rather be a collection of articles on my homepage. Thanks for the interest, and if there are other people with opinions on that topic, don't hesitate to state them!
Title: Re: Aurora
Post by: Lolilolight on January 23, 2014, 12:57:53 pm
Yes your code shows us high level programing techniques (and gives us a lot of inspiration), but it's too specific for your case, or, I need to have something who's more general.
So I don't have to use your system for the moment, but later maybe it'll be usefull.

For the moment I don't manage collisions with a dispatcher like you, I manage collisions like this :
I've a base class and derivated classes who are bounding areas. (or bounding volumes)
And I use the polymorphism to redefine the method intersects for any specific bounding areas (or bounding volumes) and to attach any kind of bounding areas or bounding volumes to an Entity. (And them I can determine if the two collisions areas or volumes of the entities intersects or not)
I do also some optimisation by storing entities into a grid structure, to avoid to do the collision test with all entities of the scene.

I've found an interesting technique to solve collisions for any king of bounding areas or bounding volumes. (The SAT theorem) witch consist of projections of normals vectors of the bounding areas to an axis. (It work for 2D and 3D shapes)
You can find easy some informations for physics like the minimal translation vector.

The link to the article : http://www.codezealot.org/archives/55 (http://www.codezealot.org/archives/55)

Later I'll cut the boundings areas or volumes into smallest bounding areas to have a hierarchy.
I'll have a more perfect collision result so.

But there are many techniques used fo collision detection (pixel perfect, bounding volumes/areas, etc...)
I like the technique presented in this following links because it works in very general cases. (2D and 3D environnements)
This is the technique who's used in many physics engines.
Title: Re: Aurora
Post by: Lo-X on January 23, 2014, 01:32:22 pm
Yes your code shows us high level programing techniques (and gives us a lot of inspiration), but it's too specific for your case, or, I need to have something who's more general.
So I don't have to use your system for the moment, but later maybe it'll be usefull.

Sorry to disagree but no it's not (for the tuple part, dispatcher part is fine IMHO). Indeed I know why to use your tuple, I think I see the point of using yours instead of std::stuple, but honnestely I don't think I have a practical case right now.

For the moment I don't manage collisions with a dispatcher like you, I manage collisions like this :
I've a base class and derivated classes who are bounding areas. (or bounding volumes)
And I use the polymorphism to redefine the method intersects for any specific bounding areas (or bounding volumes) and to [... cut the rest]

I don't want to be bad but I really do not care about the way you implement your collision detection in a thread that do not concern it. That was just a example of how to use the dispatcher. This is not a Box2d or <insert physics lib here> topic.
You still can explain us how you do your stuff on your blog or start a discussion in your threads IF it worth a thread on SFML forums.
Title: Re: Aurora
Post by: Lolilolight on January 23, 2014, 02:42:30 pm
I've just answered to his question.

Quote
I can imagine a lot of you already came in a situation where a similar technique has been necessary (see collision example), how did you solve it then?

And talked about how I solved the collisions.
Title: [Aurora] Ranges II
Post by: Nexus on February 21, 2014, 05:51:59 pm
Ranges II

I have rewritten the whole Range module and changed the design fundamentally. While the old ranges offered iteration in a rather specific way:
aurora::Range<...> range = ...;
for (; !range.empty(); range.popFront())
        std::cout << range.front() << " ";
the new ranges provide iterators, in order to fit better into existing designs such as the range-based for loop, the STL or Boost.Range:
for (auto itr = range.begin(); itr != range.end(); ++itr)
    std::cout << *itr << " ";

There are two main classes in Aurora: ranges and iterators. T is the element's value type,  C denotes the iterator traversal category. Here (http://www.bromeon.ch/libraries/aurora/v1.0/doc/group___range.html) is a link to the documentation.
template <typename T, typename C>
class Range;

template <typename T, typename C>
class RangeIterator;

Often, you won't need to specify the types explicitly, as there are maker functions you can combine with C++11 type erasure. To show what Aurora's ranges are capable of, here is a short example:
int main()
{
        // Create 3 containers with different iterator categories
        std::vector<int> v       = { 401, 402, 403 }; // random access
        std::list<int> l         = { 501, 502 };      // bidirectional
        std::forward_list<int> f = { 601 };           // forward

        // Make a range over all three containers, change each element
        for (int& i : aurora::chain(v, l, f))
                --i;

        // Output each element, using different chain order
        for (int i : aurora::chain(f, l, v))
                std::cout << i << " ";

        // Output: 600 500 501 400 401 402
}

There's still a lot of functionality missing, but the basic building blocks are there. There's also room for optimization, especially for iterators. The aurora::chain() function is already optimized excessively: it uses heavy metaprogramming to infer the appropriate range types for each sequence at compile time, and stores them in a std::tuple in a single object to avoid dynamic allocations.
Title: Re: Aurora
Post by: FRex on February 26, 2014, 01:33:24 pm
I'm interested in trying to use DoubleDispatcher but I don't understand a few things:
1. What is trampoline in traits doing, when would I need it and when would I even need own traits class?
2. What is the difference between key and id in the traits class?
3. Is it possible to not throw and not do anything if a function is not found?
4. What would happen if one or both pointers passed to call were nullptr?

Also, you're using new and delete in the example, that is very not like you so I'm reporting it as a mistake here and now. :P
Title: Re: Aurora
Post by: Nexus on February 26, 2014, 01:58:35 pm
1. What is trampoline in traits doing, when would I need it and when would I even need own traits class?
The trampolines allow you to wrap a registered function automatically. For example, when using polymorphic types, you may want to register a function
void MyFunction(Derived1& lhs, Derived2& rhs);
but the DoubleDispatcher<Base> stores
std::function<Base&, Base&>
objects. So the trampoline would downcast the arguments to match your function signature.

2. What is the difference between key and id in the traits class?
The ID is passed when registering a function with bind(), the Key is used by the map that stores the functions. Often, both types are the same, but not always -- for example, the default dispatcher uses aurora::Type<T> as ID but std::type_index as key. The reason is that the former still contains static type information (namely the T type), which is necessary to build the correct downcast.

Keep in mind that the Traits exists for advanced customization in order to provide maximum flexibility (for example, you can use an inheritance-based approach as well as a entity-component-based one). Often, it will be enough to inherit from aurora::DispatchTraits and just define the necessary functions. See also this post (http://en.sfml-dev.org/forums/index.php?topic=7330.msg96007#msg96007) to get a better overview.

3. Is it possible to not throw and not do anything if a function is not found?
Not yet, but such a fallback is planned. The problem I'm facing is the return type of call() in such a case. If it's not void, what should be returned? I don't want to restrict the used type to be default-constructible. One approach might be a specific fallback function defined by the user.

4. What would happen if one or both pointers passed to call were nullptr?
It won't work. With the default dispatcher I guess there will be a std::bad_typeid exception, but I have not tried it :)
Title: Re: Aurora
Post by: FRex on February 26, 2014, 03:05:29 pm
Third one is a very bad thing because now it throws. Not having a function to call for certain pairs of interacting object is not really exceptional situation (IMO). :(  Maybe there should be an overload that takes reference to return type as out parameter? If I run into problem with these exceptions I'm modifying the code and adding that since it's template only class and that'd take like 10 lines.

About the first: is there a default trampoline? There seems to be because functions taking derived and base or two derived get passed in to dispatcher that has functions that call on two base references and it works.
Also, if I let dispatcher be symmetric and have 'non symmetric' callbacks what will happen?
void my_callback(Derived1 * left, Derived2 * right) { /* ... */ }
dispatcher.bind(/* ect. */, &my_callback);
Derived2 one;
Derived1 two;
dispatcher.call(one, two); //does my_callback get called with one as right and two as left or not?
 
I'm guessing yes judging from these two fragments (but I still rather have a confirmation :P):
        // When symmetric, (key1,key2) and (key2,key1) are the same -> sort so that we always have (key1,key2)
        if (mSymmetric && hashValue(key2) < hashValue(key1))
                return Key(key2, key1, true);
        else
                return Key(key1, key2, false);
 
        // Call function (swap-flag equal for stored entry and passed arguments means the order was the same; otherwise swap arguments)
        if (itr->first.swapped == key.swapped)
                return itr->second(arg1, arg2);
        else
                return itr->second(arg2, arg1);
 
Title: Re: Aurora
Post by: Nexus on February 26, 2014, 10:20:01 pm
Okay, I have implemented the fallback functions. You can pass the NoOp functor if you want to do nothing:
aurora::DoubleDispatcher<B, R, Traits> dispatcher;
dispatcher.fallback( aurora::NoOp<R, 2>() );

I have also improved the dispatcher's documentation, especially the parts that were unclear for you. The new documentation is online.

There is a default trampoline in aurora::RttiDispatchTraits (the default value for the Traits template parameter), as well as a identity trampoline in aurora::DispatchTraits (the base class for custom traits).

Yes, dispatchers will automatically figure out the correct order of arguments when symmetry is enabled. If necessary, the arguments are reordered during the call.
Title: Re: Aurora
Post by: Nexus on June 06, 2014, 11:08:12 am
I changed the template parameters of the dynamic dispatchers. The user can specify the function type (the signature) as the first template argument, which is more intuitive and closer to std::function.
aurora::SingleDispatcher<void(Base&)> dispatcher;
dispatcher.bind(aurora::Type<Derived>(), [] (Derived&) { std::cout << "D"; });

Derived d;
Base& ref = d;
dispatcher.call(ref); // output: D

It is now also possible to pass additional arguments when invoking the function. Its type can be specified as an additional parameter in the signature.
aurora::SingleDispatcher<void(Base&, int)> dispatcher;
dispatcher.bind(aurora::Type<Derived>(), [] (Derived&, int i) { std::cout << "D" << i; });

Derived d;
Base& ref = d;
dispatcher.call(ref, 5); // output: D5