You are not logged in.

#1 2016-09-01 03:18:04

zerophase
Member
Registered: 2015-09-03
Posts: 229

How to Implement bracket container initialization for non-stl C++?

I'm working in Unreal, and sadly or gladly stl isn't supported in Engine. They have the TArray class that's basically a vector, with maybe some more optimizations for what's needed in games.  At any route Unreal doesn't support stl. I'm generating a bunch of code and would prefer to not have to first create a TArray<FName> to pass into my constructors, and would prefer bracket notation just to make everything a bit simpler and more concise. 

Considering bracket notation can't be implemented with overrides; I was wondering if anyone knows how bracket notation was added in C++ 11. (looking for reading on it) Just trying to figure out if there is a way to hack it into Unreal.

Just to make it clear what I mean.

void C_Plus_Plus_11(std::vector<int> test)
{
	for (const auto t : test)
	{
		std::cout << t << std::endl;
	}
}

void Unreal(TArray<int> test)
{
   // do stuff here
}

int main()
{
	// this works.
	C_Plus_Plus_11({1,2,3,4});

        // this doesn't
        Unreal({1,2,3,4});
}

Offline

#2 2016-09-01 05:33:43

bullet
Member
Registered: 2016-08-04
Posts: 18

Re: How to Implement bracket container initialization for non-stl C++?

First off, games usually don't use the STL because performance and what not. Pretty much nothing relying on very high speed is using STL stuff.

Second, quick Google showed that a special constructor is required to support initializer lists, it gets one argument being std::initializer_list and your ctor knows what to do, so I'm afraid you can't add it yourself.

Offline

#3 2016-09-01 07:06:27

zerophase
Member
Registered: 2015-09-03
Posts: 229

Re: How to Implement bracket container initialization for non-stl C++?

bullet wrote:

First off, games usually don't use the STL because performance and what not. Pretty much nothing relying on very high speed is using STL stuff.

Second, quick Google showed that a special constructor is required to support initializer lists, it gets one argument being std::initializer_list and your ctor knows what to do, so I'm afraid you can't add it yourself.

Yeah, I didn't think I'd be able to. What did you search for on Google? I tried looking, and couldn't find the precise answer.

I'm trying to implement something similar with variadic functions. Pretty sure what I have isn't quite right. This is going to take awhile to figure out with long compile times.

Offline

#4 2016-09-02 04:11:09

mpan
Member
Registered: 2012-08-01
Posts: 1,205
Website

Re: How to Implement bracket container initialization for non-stl C++?

No need to quote a person just above your post. It’s kinda obvious you’re not answering yourself or someone 10 pages back in the thread.

If ::std::initializer_list is supported, the code below shows an example:

#include <initializer_list>
#include <iostream>
#include <cstdlib>

class Example
{
    public:
        Example(::std::initializer_list<int> values);
};

Example::Example(::std::initializer_list<int> values)
{
    using ::std::cout;
    cout << "[DEBUG] Received " << values.size() << " values";
    if (values.size() > 0)
    {
        auto it = values.begin();
        cout << ": {" << *it++;
        while (it != values.end())
        {
            cout << ", " << *it++;
        }
        cout << "}\n";
    }
    else
    {
        cout << ".\n";
    }
}

int main()
{
    Example test1 = {};
    Example test2 = {3};
    Example test3 = {3, 1, 4, 1};
    
    return EXIT_SUCCESS;
}

Otherwise there is no way to do it. The furthest you may go is something like this:

#include <iostream>
#include <cstdlib>

class Example
{
    public:
        template <::std::size_t n>
            Example(int const (&init)[n]);
};

template <::std::size_t n>
    Example::Example(int const (&init)[n])
    {
        using ::std::cout;
        int const* it = init;
        cout << "[DEBUG] Received " << n << " values {" << *it++;
        while (it != init + n)
        {
            cout << ", " << *it++;
        }
        cout << "}\n";
    }

int main()
{
    static int const initializer1[] = {3};
    static int const initializer2[] = {3, 1, 4, 1};
    Example test2 = initializer1;
    Example test3 = initializer2;
    
    return EXIT_SUCCESS;
}

Note however that this solution is efficient in terms of program size¹, if used often. This is because a separate piece of code is generated for each use with different “initializer” size.

There is also a third way, but also has the same drawback as the one above. Instead of using intermediate array, one may create constructors for each possible size. The code would be pretty easy to write with Boost.Preprocessor²:

#include <iostream>
#include <cstdlib>
#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>

#define EXAMPLE_INITIALIZER_MAX_ELEMENTS 16

class Example
{
    public:
        #define EXAMPLE_GEN_PRINT(z, n, aux) \
            cout << ", " << BOOST_PP_CAT(v, BOOST_PP_INC(n));
        #define EXAMPLE_GEN_CTOR(z, n, aux) \
            BOOST_PP_IF(BOOST_PP_EQUAL(1, n), explicit, ) \
            Example(BOOST_PP_ENUM_PARAMS(n, int v)) { \
                using ::std::cout; \
                cout << "[DEBUG] Received " << (n) << " values"; \
                BOOST_PP_IF(n, \
                  cout << ": {" << v0; \
                  BOOST_PP_REPEAT(BOOST_PP_DEC(n), EXAMPLE_GEN_PRINT, ~) \
                  cout << "}\n"; \
                , \
                  cout << ".\n"; \
                ) \
            }
        BOOST_PP_REPEAT(BOOST_PP_INC(EXAMPLE_INITIALIZER_MAX_ELEMENTS), \
          EXAMPLE_GEN_CTOR, ~)
        #undef EXAMPLE_GEN_PRINT
        #undef EXAMPLE_GEN_CTOR
};

int main()
{
    Example test1;
    Example test2(3);
    Example test3(3, 1, 4, 1);
    
    return EXIT_SUCCESS;
}

Be sure to not put brackets after no-argument version by accident — this would create an expression that indicates not a declaration of an object of type Example, but a declaration of a global function named test1 that takes no arguments and returns Example.

Lack of STL in games has nothing to do with performance. STL is at least as good as the same constructs writen manually. I’m also suspecting that we’re talking about game logic here, not the engine core. Good time performance is unnecessary for that. So unimportant that games use either scripting languages or poorly written, slow virtual machines (this includes, for example Quake series and descendants). The reason STL isn’t used is that it requires two conditions to be met:

  • The compiler must support C++. C++, not “few parts of C++”.

  • Someone has to implement the whole library for that particular platform. And STL is an utterly complex beast.

There is also another motivation for avoiding STL: it would require enormous amount of glue.
____
¹ Which also implies that time performance of the whole program may be harmed due to cache thrashing.
² At least to 255 arguments, which is way above you should ever need for in-line initialization.

Last edited by mpan (2016-09-02 05:02:22)


Sometimes I seem a bit harsh — don’t get offended too easily!

Offline

#5 2016-09-02 05:59:27

zerophase
Member
Registered: 2015-09-03
Posts: 229

Re: How to Implement bracket container initialization for non-stl C++?

Yeah, performance was the issue back in the 90s and early 2000s with stl. Now, it's just platform support and compile times. For Unreal there's also most likely a massive amount of code needing rewrites at this point to bring stl in.

As for my actual issue unreal fixed it by releasing 4.13, which supports initializer lists the day after I asked about this.

Offline

Board footer

Powered by FluxBB