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

Author Topic: Aurora  (Read 29802 times)

0 Members and 1 Guest are viewing this topic.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Aurora
« Reply #15 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.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Aurora
« Reply #16 on: April 08, 2012, 07:05:55 pm »
Legit reasons, thanks for pointing them out. To be honest I didn't thought deep enough. :)

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Aurora
« Reply #17 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"));
« Last Edit: September 16, 2012, 10:21:28 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

mateandmetal

  • Full Member
  • ***
  • Posts: 171
  • The bird is the word
    • View Profile
    • my blog
Re: Aurora
« Reply #18 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
- Mate (beverage) addict
- Heavy metal addict _lml
- SFML 2 addict
- My first (and free) game: BichingISH!

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Aurora
« Reply #19 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... ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Aurora
« Reply #20 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.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
[Aurora] Enter Ranges
« Reply #21 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:
  • The type of the underlying iterator needn't be known at range declaration, therefore unnecessary compiletime dependencies are avoided. Also, an algorithm for a specific element type can be written as a non-template-function while still being generic.
  • The range may operate on different underlying iterator types, allowing iteration over different containers in a single run.
  • Code becomes simpler, since the implementation details can be hidden.
  • On the downside, type erasure comes with a small memory and speed overhead. Therefore, type-erased ranges pay out in situations where a lot of work per element has to be done.
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() << ' ';
}
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Lo-X

  • Hero Member
  • *****
  • Posts: 618
    • View Profile
    • My personal website, with CV, portfolio and projects
Re: Aurora
« Reply #22 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). :)

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Aurora
« Reply #23 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?
« Last Edit: October 26, 2012, 09:24:04 pm by FRex »
Back to C++ gamedev with SFML in May 2023

Lo-X

  • Hero Member
  • *****
  • Posts: 618
    • View Profile
    • My personal website, with CV, portfolio and projects
Re: Aurora
« Reply #24 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 =)

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Aurora
« Reply #25 on: October 26, 2012, 10:19:15 pm »
So I've thought. Too bad.
Back to C++ gamedev with SFML in May 2023

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
[Aurora] More flexible dispatchers
« Reply #26 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 ;)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Aurora
« Reply #27 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();
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Lolilolight

  • Hero Member
  • *****
  • Posts: 1232
    • View Profile
Re: Aurora
« Reply #28 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 ?
« Last Edit: January 15, 2014, 09:41:53 am by Lolilolight »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Aurora
« Reply #29 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.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

 

anything