Converting a MPL Vector to a Static Array
Asked Answered
C

2

5

I wrote some code to generate a boost::mpl::vector to use as a lookup table for a factorial function, as a test for a more general library function with which a developer may be able to generate a lookup table in the form of a static array of primitives. The function (which would most probably be implemented as a preprocessor macro definition) would accept the name and size of the array to be initialized, as well the name of a class template to be used as the metafunction to initialize each element i of the array.

I thought that the best way to go about doing this without the use of external scripts would be to

  1. Create a boost::mpl::vector, as is done in the code listing below, and push the return value of the user-supplied metafunction for each element of the array to the back of the vector;
  2. Use the elements of the vector initialize the static array (perhaps by using a series of macros, the last of which would use the __VARARGS__ macro to accomplish this).

I know neither how I would accomplish (2) nor whether the procedure I describe is a good way of doing what I seek. Here are the following questions for which I would like answers:

  1. Is my procedure a good way of accomplishing what I seek? If not, please describe a better procedure which would accomplish the same thing, without the use of external scripts.

  2. If my procedure is indeed a good way of accomplishing what I seek, how would I implement (2)?

    I will be sure to post a link to the source file containing library function which I describe once I implement it. The code listing follows below.

    namespace mpl = boost::mpl;

    template <typename x>
    struct factorial:
        mpl::if_<mpl::greater<x, mpl::int_<1>>,
            mpl::multiplies<x, factorial<x::prior>>,
            mpl::int_<1>
        >::type
    {};
    
    template <typename sequence, typename size>
    struct compileTable:
        mpl::if_<mpl::greater<size, mpl::int_<0>>,
            compileTable<
                mpl::push_front<sequence, factorial<size>>::type,
                size::prior
            >,
            sequence
        >::type
    {};
    
    static const int TABLE_SIZE = 13;
    
    typedef compileTable<
        mpl::vector<>,
        mpl::int_<TABLE_SIZE>
    >::type factorialTable;
    
    /*
    ** This is where I am stuck; how would I use the elements
    ** of factorialTable to initialize a static array?
    */
    
Churchill answered 25/2, 2011 at 1:25 Comment(4)
use boost.org/doc/libs/1_45_0/libs/mpl/doc/refmanual/for-each.htmlFustian
I am a little confused here - since for-each is a runtime algorithm, how would I use it to initialize a static array?Churchill
what exactly do you mean by static array? static int foo[]?Fustian
Sorry - to clarify, I mean a static constant array that is initialized during compile time, like static int * const FOO = { ... }Churchill
F
9

http://www.boost.org/doc/libs/1_46_0/libs/preprocessor/doc/index.html

#define MACRO(z, i, data) \
    mpl::at_c<data,i>::value

static const data[] = { BOOST_PP_ENUM(N, MACRO, factorialTable) };
Fustian answered 25/2, 2011 at 2:22 Comment(0)
C
1

Here is the source code for the file containing the library function, as promised; please be sure to read the remarks I have made below the code listings. Thanks again to aaa for his help in showing me how to initialize a static array using BOOST_PP_ENUM!

Source code for xi/mpl/lut.h:

#ifndef __XI_LUT_INCLUDED__
#define __XI_LUT_INCLUDED__

#ifndef __cplusplus
    #error The file __FILE__ requires a C++ compiler in order to be successfully compiled.
#endif

#include <boost/mpl/apply.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/preprocessor/repetition/enum.hpp>

#define __XI_LUT_SET_INDEX(z, n, sequence) \
    mpl::at_c<sequence, n>::type::value

#define __XI_GENERATE_LUT_IMPL(function, tableType, tableName, tableSize) \
    \
    template <typename sequence, typename size> \
    struct __compileTable_##function##_##tableSize##: \
        mpl::if_<mpl::greater<size, mpl::int_<0>>, \
            __compileTable_##function##_##tableSize##< \
                mpl::push_front<sequence, \
                    mpl::apply< \
                        function##<mpl::_>, \
                        size \
                    >::type>::type, \
                size::prior \
            >, \
            sequence \
        >::type \
    {}; \
    \
    typedef __compileTable_##function##_##tableSize##< \
        mpl::vector<>, \
        mpl::int_<##tableSize##> \
    >::type __compiledTable_##function##_##tableSize##; \
    \
    static const tableType tableName##[] = { \
        BOOST_PP_ENUM( \
            tableSize##, \
            __XI_LUT_SET_INDEX, \
            __compiledTable_##function##_##tableSize## \
        ) \
    }

#define XI_GENERATE_LUT(function, tableType, tableName, tableSize) \
    __XI_GENERATE_LUT_IMPL(function, tableType, tableName, tableSize)   

#endif

Source code for a useful test file:

#include <boost/mpl/greater.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/placeholders.hpp>
#include <cstdio>
#include <xi/mpl/lut.hpp>

namespace mpl = boost::mpl;

template <typename x>
struct factorial:
    mpl::if_<mpl::greater<x, mpl::int_<1>>,
        mpl::multiplies<x, factorial<x::prior>>,
        mpl::int_<1>
    >::type
{};

XI_GENERATE_LUT(factorial, int, FACTORIAL_TABLE, 4);

int main(int argc, char ** argv) {

    // This should print '24:'
    printf("Result: %d.\n", FACTORIAL_TABLE[3]);
    return 0;

}

I will refrain from providing a URL to the file for now so that I can continue to edit the code listing. I am confident that the code can be improved for purposes of compatibility, so it is definitely not in a final state. Here are some known issues:

  1. The code will not compile on MSVC 9.0.
  2. Attempting to create a lookup table of a particular size for a metafunction name after one has already been created of the same size and for the same metafunction name will result in an error, since corresponding types and templates would be defined for these parameters. I do not want to use __COUNTER__ to alleviate this problem since it is a nonstandard macro definition.

I have not tried compiling this code on any other compilers except ICC and MSCV, and would like to know how GCC handles it - please let me know of any issues which arise so that proper recourse may be taken. I will post a URL to the file once the code works with little trouble on most major compilers. Any feedback would be greatly appreciated!

Churchill answered 25/2, 2011 at 4:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.