简体   繁体   中英

using std::index_sequence to initialize POD struct container with fixed size array members

I am trying to use std::integer_sequence and its helper template std::index_sequence to initialize a fixed size POD struct that behaves like a container. Each element in turn contains a fixed size array field.

The container POD struct is essentially defined as:

#define ELEM_NAME_SIZE   7
#define CONTAINER_SIZE  20

using Container = struct Container {
    int entries;
    Element elems[MAX_CONTAINER_SIZE];    
};

and the individual elements are:

using Element = struct Element {
    char name[MAX_NAME_CHARS];
    bool bFlag;
};

Building on the answer to question , I was able to use std::index_sequence to initialize the fixed length Element s. So far so good.

I need to come up with a way to construct the Container with either a single fixed size const char* - say "ABCDEF", or alternatively with an array of fixed length (up to a max of ELEM_NAME_SIZE) character strings.

constexpr char gTestNames[3][ELEM_NAME_SIZE] = {
    "APPLE", "BEE", "CHAIN"
};

In the live coliru code , the first of these container constructors is as follows:

template<std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr Container makeContainer(char const (&name)[N]) {
    return makeContainer_in(name, Indices{});
}

constructing and printing out the contents of the container with this yield:

const auto container = makeContainer("ABCDEF");
std::cout << container << '\n';

with the output:

Container: entries(7)[Element:[ABCDEF],,Element:[],,Element:[],,Element:[],,Element:[],,Element:[],,Element:[],,]

However, using the alternative template function overload, with parameter gTestNames :

template<std::size_t N, std::size_t NAME_LEN_MAX = ELEM_NAME_SIZE, typename Indices = std::make_index_sequence<N>>
constexpr Container makeContainer(const char(&names)[N][NAME_LEN_MAX]) {
    return makeContainer_in(names, Indices{});
}

Called using:

const auto container1 = makeContainer(gTestNames);
std::cout << container1 << '\n';;

I get the following error output:

main.cpp: In instantiation of 'constexpr Container makeContainer(const char (&)[N][NAME_LEN_MAX]) [with long unsigned int N = 3; long unsigned int NAME_LEN_MAX = 7; Indices = std::integer_sequence<long unsigned int, 0, 1, 2>; Container = Container]':
main.cpp:78:53:   required from here
main.cpp:64:28: error: no matching function for call to 'makeContainer_in(const char [3][7], std::integer_sequence<long unsigned int, 0, 1, 2>)'
   64 |     return makeContainer_in(names, Indices{});
      |            ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
main.cpp:52:21: note: candidate: 'template<long unsigned int N, long unsigned int ...Is> constexpr Container makeContainer_in(const char (&)[N], std::index_sequence<Is ...>)'
   52 | constexpr Container makeContainer_in(char const (&packed)[N], std::index_sequence<Is...>) {
      |                     ^~~~~~~~~~~~~~~~
main.cpp:52:21: note:   template argument deduction/substitution failed:
main.cpp:64:28: note:   mismatched types 'const char' and 'const char [7]'
   64 |     return makeContainer_in(names, Indices{});
      |            ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~

If possible, besides correcting the mistake I have, is there some way I could simplify all this boiler plate code - perhaps using fold expressions - I would like to use index_sequences (as I am trying to learn how to use them), but I never understood the requirement to always forward the calls to a sort of proxy in order to expand the indices. There has got to be an easier way.

Well of course:

template<std::size_t N, std::size_t... Is>
constexpr Container makeContainer_in(char const (&packed)[N], std::index_sequence<Is...>) {
     return Container{ N, { packed[Is]... } };
}

is not going to work, a Container has an Element , not char s (and an Element has no Element::Element(char) constructor). Just instantiate the actual container element using it's constructor inside the parameter pack.

template<std::size_t N, std::size_t... Is>
constexpr Element makeElement_in(char const (&name)[N], std::index_sequence<Is...>) {
    return Element{ { name[Is]... }, true, };
}

template<std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr Element makeElement(char const (&name)[N]) {
    return makeElement_in(name, Indices{});
}

template<std::size_t N, std::size_t NAME_LEN_MAX, std::size_t... Is>
constexpr Container makeContainer_in(const char(&names)[N][NAME_LEN_MAX], std::index_sequence<Is...>) {
    return Container{ N, { makeElement(names[Is])... } };
}

template<std::size_t N, std::size_t NAME_LEN_MAX, typename Indices = std::make_index_sequence<N>>
constexpr Container makeContainer(const char(&names)[N][NAME_LEN_MAX]) {
    return makeContainer_in(names, Indices{});
}

Godbolt link .

if you want the output following output given HELLO

 Element:[H], Element:[E], Element:[L], Element:[L], Element:[O],

this code will work, although there will be an element for the null character if you pass a null terminated string.

#include <iostream>
#include <tuple>

#define MAX_NAME_CHARS 9
#define MAX_CONTAINER_SIZE 100

struct Element {
    char name[MAX_NAME_CHARS];
    bool bFlag;
    int foo;
    friend std::ostream& operator << (std::ostream& os, const Element& next) {
        os << next.name;
        return os;
    }    
};

struct Container {
    int entries;
    Element elems[MAX_CONTAINER_SIZE];    
    friend std::ostream& operator << (std::ostream& os, const Container& next) {        
            os << "Container: entries(" << next.entries << ")[";
            for (auto i = 0; i<next.entries; ++i) {
                os << next.elems[i] << ",";
            }
            os << "]\n";
            return os;
    } 
};

template<std::size_t N, std::size_t ... I>
constexpr Container makeContainerInSingle(const char(&singlecharnames)[N],
                                 std::index_sequence<I...>) {
    auto result = Container {
        N,
        {Element{
            {singlecharnames[I]},
            true, 
            0}...
        }
    };
    return result;
}

template<std::size_t N>
constexpr Container makeContainerSingle(const char(&singlecharnames)[N]) {
    return makeContainerInSingle(singlecharnames, std::make_index_sequence<N>{});
}

int main() {
    auto c2 = makeContainerSingle("HELLO");
    std::cout << c2 << std::endl;
}

output is the following

Container: entries(6)[H,E,L,L,O,,]

as an additional note, im not sure what, if anything, can be done to simplify the pattern using std::index_sequence although the index sequence can be constructed directly as in my example code if you consider that more simple.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM