Variadic templates and std::initializer_lists

This is a discussion on Variadic templates and std::initializer_lists within the C++ Programming forums, part of the General Programming Boards category; I was experimenting with something today. Basically, what I want to is fill an array of N nodes . Each ...

  1. #1
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,622

    Variadic templates and std::initializer_lists

    I was experimenting with something today.
    Basically, what I want to is fill an array of N nodes.
    Each node contains vectors in, out an defs of type std::string, and then one called jumps of type int:

    Code:
    struct XNode
    {
    	std::vector<std::string> def, in, out;
    	std::vector<int> jumps;
    
    	bool operator == (const XNode& rhs) const
    	{
    		return
    		(
    			def == rhs.def &&
    			in == rhs.in &&
    			out == rhs.out &&
    			jumps == rhs.jumps
    		);
    	}
    };
    
    std::array<XNode, 14> Nodes;
    Now, since I am using Visual Studio, initializer lists are not fully supported yet, which means I can't initialize the array directly.
    So, I made a function FillNodes.
    But here is the problem: the function needs to take a variable amount of arguments (sounds like a good candidate for initializer lists), of varying types (sounds like a good candidate for variadic templates).
    So basically, something like

    Code:
    template<int N, typename... NodeArgs_t>
    void FillNodes(std::array<XNode, N>& Nodes, NodeArgs_t... NodeArgs)
    // ...
    But then I find out that initializer lists, for some reason, can't be deduced as template arguments.
    I was initially planning on something like

    Code:
    FillNodes(Nodes,
    {
    	{ { "c", "d" }, { "r3", "r2" }, { "r2", "r1" } }, { 10, 11 },
    	// ...
    });
    But obviously this won't work since I can't pass initializer lists to variadic functions.
    I experiemented with wrapping the initializer lists in a struct:

    Code:
    template<typename T>
    struct hack
    {
    	hack(const std::initialier_list<T>& list): m_list(list) {}
    	std::initializer_list<T> m_list;
    };
    
    auto test = hack<std::initializer_list<std::string>>({ { "c", "d" }, { "r3", "r2" }, { "r2", "r1" } });
    ...But that crashed the compiler everytime I tried to do something. I'm not even sure if this is possible! Commenting out pretty much everything but the constructor still makes the compiler crash.

    So I'm out of ideas. Anyone have any good suggestions on making this nift piece of code work?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    2,760
    You have probably already considered them, but how about creating and using constructors for your classes?

    Jim

  3. #3
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,622
    I have only one structure and that's XNode. I could use a constructor there, taking initializer lists, but the problem remains that I can't forward them there.
    The VS Standard Library doesn't yet support initializer lists. That means no initializing the std::array directly.
    So I'm not sure what you propose, exactly...?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,709
    Is there some reason that the array class can't be used in the following fashion:
    Code:
    array<XNode, 14> test = { XNode("c", "d"), XNode("r3", "r2"), XNode("r2", "r1") };
    I realise this is simple, but...

  5. #5
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,622
    Number of parameters is variable, hence the use of a initializer list. They're used to push back data directly into the vectors in XNode:
    Code:
    XNode AddNode(std::initializer_list<std::string> Defs, std::initializer_list<std::string> In,
    			 std::initializer_list<std::string> Out, std::initializer_list<int> Jumps)
    {
    	XNode tmp;
    	for (auto& elem : Defs)
    		tmp.def.push_back(elem);
    	for (auto& elem : In)
    		tmp.in.push_back(elem);
    	for (auto& elem : Out)
    		tmp.out.push_back(elem);
    	for (auto& elem : Jumps)
    		tmp.jumps.push_back(elem);
    	return tmp;
    }
    I may have gotten this to work, though, or at least getting there. I am no longer getting crashes.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  6. #6
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,709
    It seems it would be useful if constructors supported variadic arguments then. I can't remember if C++ supports that at all or not. If it does not, perhaps some variation of the named constructor idiom is in order.

  7. #7
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,622
    They do, but VS's implementation is incomplete.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,709
    Such is life

  9. #9
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,622
    So it is.
    I managed to get it working. So here is the code for those stuck with the same problem:
    Code:
    #include <iostream>
    #include <vector>
    #include <array>
    #include <initializer_list>
    #include <assert.h>
    #include <string>
    
    struct XNode
    {
    	std::vector<std::string> def, in, out;
    	std::vector<int> jumps;
    };
    
    template<typename T>
    struct list
    {
    	list(std::initializer_list<T> list): m_list(list) {}
    	std::initializer_list<T> m_list;
    };
    typedef list<std::initializer_list<std::string>> lists;
    typedef list<int> listn;
    
    XNode AddNode(std::initializer_list<std::string> Defs, std::initializer_list<std::string> In,
    			 std::initializer_list<std::string> Out, std::initializer_list<int> Jumps)
    {
    	XNode tmp;
    	for (auto& elem : Defs)
    		tmp.def.push_back(elem);
    	for (auto& elem : In)
    		tmp.in.push_back(elem);
    	for (auto& elem : Out)
    		tmp.out.push_back(elem);
    	for (auto& elem : Jumps)
    		tmp.jumps.push_back(elem);
    	return tmp;
    }
    
    template<int N>
    void FillNodesRec(std::array<XNode, N>&, int) {}
    
    template<int N, typename... NodeArgs_t>
    void FillNodesRec(std::array<XNode, N>& Nodes, int index, const lists& Data1, const listn& Data2, NodeArgs_t... NodeArgs)
    {
    	assert(Data1.m_list.size() == 3); // There shall be exactly 3 arguments: defs, ins and outs
    	Nodes[index] = AddNode(Data1.m_list.begin()[0], Data1.m_list.begin()[1], Data1.m_list.begin()[2], Data2.m_list);
    	FillNodesRec(Nodes, index + 1, NodeArgs...);
    }
    
    template<int N, typename... NodeArgs_t>
    void FillNodes(std::array<XNode, N>& Nodes, NodeArgs_t... NodeArgs)
    {
    	static_assert(sizeof...(NodeArgs_t) == N*2, "Number of nodes passed must equal number of nodes in the array.");
    	FillNodesRec(Nodes, 0, NodeArgs...);
    }
    
    int main()
    {
    	std::array<XNode, 14> Nodes;
    	// Jumps back edges are 1-indexed.
    	FillNodes(Nodes,
    		lists({ { "c" },		{ "r3" },		{} }), listn({ }),
    		lists({ { "p" },		{ "r1" },		{} }), listn({ 1 }),
    		lists({ {},			{ "p" },		{} }), listn({ 2 }),
    		lists({ { "r1" },		{ "p" },		{} }), listn({ 3 }),
    
    		lists({ { "r1", "r2" },	{ "r1" },		{} }), listn({ 4 }),
    		lists({ { "s" },		{ "r1" },		{} }), listn({ 5 }),
    		lists({ { "r1" },		{ "p" },		{} }), listn({ 6 }),
    		lists({ { "r1", "r2" },	{ "r1" },		{} }), listn({ 7 }),
    
    		lists({ { "t" },		{ "r1" },		{} }), listn({ 8 }),
    		lists({ { "u" },		{ "s", "t" },		{} }), listn({ 9 }),
    		lists({ { "u" },		{},			{} }), listn({ 3 }),
    		lists({ { "r1" },		{ "u" },		{} }), listn({ 11, 10 }),
    
    		lists({ { "r3" },		{ "c" },		{} }), listn({ 12 }),
    		lists({ {},			{ "r1", "r3" },	{} }), listn({ 13 })
    	);
    For some reason, taking the initializer list in list by (const) reference makes the compiler crash.
    It's still possible to take the ones in AddNode by const reference.
    Last edited by Elysia; 11-28-2012 at 11:17 AM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How to use variadic functions
    By Richardcavell in forum C Programming
    Replies: 1
    Last Post: 03-26-2011, 06:38 AM
  2. Overloading a variadic macro
    By Richardcavell in forum C Programming
    Replies: 6
    Last Post: 03-14-2011, 06:24 PM
  3. Variadic function
    By Richardcavell in forum C Programming
    Replies: 17
    Last Post: 03-01-2011, 12:34 AM
  4. variadic templates
    By Elkvis in forum C++ Programming
    Replies: 2
    Last Post: 05-11-2009, 03:57 PM
  5. Variadic Functions
    By coder8137 in forum C Programming
    Replies: 9
    Last Post: 01-12-2007, 07:25 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21