2

I need a generic way to create a tuple of N integers in runtime where N is the size of a vector (not more than 20). For example:

int main() {
    vector<int> vec;
    vec.push_back(31);
    vec.push_back(24);

    auto x = MakeTuple(vec.size());    // x should have the type tuple<int,int>

    return 0;
}

EDIT: Just to clarify ... I cannot stick to vectors because I need tuple types to create objects of TBB Flow graph template classes using lambda expressions.

EDIT 2: Obviously, I need to explain the problem. I have unstructured meshes which I dynamically partition to X partitions. Each of these partitions is connected with N other partitions, where N > 1. These connections define dependencies between partitions which I need to use to build a flow graph. Since the structure of the mesh is known only in runtime, so is the dependence structure. Thus, I need a way to create specific graph nodes (TBB classes) which take tuples that represent input dependencies as template parameters.

Here is an example how to create a TBB function node that has two input dependencies:

using namespace tbb:flow;

function_node<tuple<int,int> > *f = 
    new function_node<tuple<int,int>>(g, 1, [=] (tuple<int,int> input) -> void { ... });

join_node<tuple<int,int> *j = new join_node<tuple<int,int>> (g);

make_edge(*j, *f);

Now, lets say that we partition the mesh to 10 partitions. We determine dependencies for each partition by checking the list of edges. If there is an edge that connects two partitions, say part1 and part2, then there is a dependency between these partitions. I collect dependency information using per-partition vectors. Now the problem appears: I need to use this dependency information (stored in vectors) to create a graph of function nodes that need proper tuple types based on the number of partition dependencies. For example, if part1 and part2 have following dependencies [2,4,5,9] and [1,3], then I need these two function nodes:

function_node<tuple<int,int> > *f1 = 
    new function_node<tuple<int,int>>(g, 1, [=] (tuple<int,int> input) -> void { ... });

function_node<tuple<int,int,int,int> > *f2 = 
    new function_node<tuple<int,int,int,int>>(g, 1, [=] (tuple<int,int,int,int> input) -> void { ... });
Anton
  • 6,349
  • 1
  • 25
  • 53
Diggy
  • 230
  • 2
  • 11
  • 2
    There is no way to choose a type at run-time. –  Jan 02 '14 at 05:38
  • `I need a generic way to create a tuple of N integers in runtime` No, you don't. Stop XYing us! – Lightness Races in Orbit Jan 02 '14 at 05:40
  • _(variant)_ `I need a generic way to create a tuple of N integers in runtime` That would be fine, but this code is not "a generic way". – Lightness Races in Orbit Jan 02 '14 at 05:42
  • C++14? How about `MakeTuple(vec.size(),[&](auto x){ // x is `tuple` }`)? – Yakk - Adam Nevraumont Jan 02 '14 at 06:07
  • How many such tuples? – Yakk - Adam Nevraumont Jan 02 '14 at 06:08
  • For the time being I think I would be happy with tuples that have from 1 to 10 elements. – Diggy Jan 02 '14 at 06:15
  • @Yakk I don't understand what you are suggesting. And why does the number of tuples matter? – jogojapan Jan 02 '14 at 06:24
  • @jogojapan it was not an answer, so not surprising it is not clear ;). Number of tuples matters because the possible answer has exponential overhead in the number of tuples (in scope at once with independent sizes), so is merely ridiculous at 1 tuple, and utterly infeasible with many more. Really just an exercise by me to show you can do it: I strongly suspect the OP is barking up the wrong tree. – Yakk - Adam Nevraumont Jan 02 '14 at 06:48
  • TBB? The Flow graph classes: why do they need tuples? – Yakk - Adam Nevraumont Jan 02 '14 at 06:54
  • For all function or multifunction TBB nodes that have more than one input you need to have a join node that by design creates a tuple and provides it as an input to the function node. – Diggy Jan 02 '14 at 07:26
  • @Yakk I'd still like to see that potential solution. How can I go from a run-time parameter to a compile-time parameter, even in C++14? – jogojapan Jan 02 '14 at 08:03
  • What is TBB -- three letter acronyms are not a unique name space? Who wrote TBB Flow graph classes? Why is the tuple design the only choice? – Yakk - Adam Nevraumont Jan 02 '14 at 12:46
  • @jogojapan here is an implementation of magic switch that does not exactly correspond to this problem: http://stackoverflow.com/questions/16067537/can-i-separate-creation-and-usage-locations-of-compile-time-strategies – Yakk - Adam Nevraumont Jan 02 '14 at 13:24
  • http://www.threadingbuildingblocks.org/docs/help/reference/flow_graph/func_node_cls.htm shows no need for `Input` to be a `std::tuple`. Make `Input` a `std::vector` instead of a `std::tuple`. – Yakk - Adam Nevraumont Jan 02 '14 at 16:36
  • @Yakk Please read the problem and my previous explanation why you need tuples. Function nodes take one input (any type), but if you want to have a function node that takes more than one input you have to use join nodes that create tuples on their output ports. – Diggy Jan 02 '14 at 18:09
  • @Diggy Then build a graph like this: `int` -> `std::vector` { -> `std::tuple< std::vector, std::vector >` -> `std::vector` } and loop the `{}` block. Should require logarithmic depth to handle any branching factor, with an output of `std::vector`? Each input produces a single `int`, which then is converted to a `std::vector` (or just directly produce said `vector`s). You use `join_node` on two `std::vector` to produce a `std::tuple< std::vector, std::vector >`. You then write a converting node that takes that and outputs a `std::vector`. – Yakk - Adam Nevraumont Jan 02 '14 at 18:24
  • Then you just build a relatively balanced tree of such things, feeding them into a single `std::vector` accumulated output. Or am I missing something? I am not a TBB expert. – Yakk - Adam Nevraumont Jan 02 '14 at 18:27
  • The meaning of join node ports is that they collect data on each port asynchronously and independently. Once there is at least a single piece of data received on all ports, the node joins them into a tuple and sends it to all successors. So, what you suggest would defeat the purpose of using TBB flow graph in the first place, which is to handle all synchronization instead of a programmer. In your solution, you as a programmer would have to explicitly check if all ports have received the data, and only then send the vector to successors. – Diggy Jan 03 '14 at 01:49
  • @Yakk Ok -- but the magic-switch you are proposing is essentially the same as what norlesh proposes in the answer (except that the syntax there is incorrect). It's a run-time cascade of if-statements (assuming the compiler properly optimizes-out the tail-recursion). Admittedly, it would be less error-prone than lots of hand-written if-statements, and look neat in the code. It doesn't require C++14, though, right? – jogojapan Jan 03 '14 at 02:07
  • @jogojapan no, but `auto` lambda arguements do. The boilerplate is half or smaller in 14. – Yakk - Adam Nevraumont Jan 03 '14 at 02:13

2 Answers2

1

Tuple "dimensions" are part of their type (via templates), and you cannot choose a type at runtime from dynamically-chosen values.

Stick with your vector.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

Since it must be a tuple, and you do have a limit on the length then you could use a factory method such as the following psudo code

generic_type MakeTuple(size_t n) {
    switch(n) {
        case 1:
            return make_tuple(int);
        case 2:
            return make_tuple(int, int);
        ...
        default:
            throw 'oh crap';
}
norlesh
  • 1,749
  • 11
  • 23
  • I'd suggest the same method. However, you need to get the vector in as an argument and supply the correct arguments to the `make_tuple` function. – jogojapan Jan 02 '14 at 06:04
  • that's where our old friend boost::make_tuple comes in 7;^) or not? – norlesh Jan 02 '14 at 06:06
  • So this was the only thing that I could really think of that would work, but it is certainly not generic. I am not saying that generic way exists, but I am wondering if there is more graceful way to do this. – Diggy Jan 02 '14 at 06:11
  • if it would work then hide it behind an interface and move on to more pressing problems that wont work until a better solution comes along – norlesh Jan 02 '14 at 06:13
  • This does not answer the problem posed, as `x` will carry no non-type-erased type information. – Yakk - Adam Nevraumont Jan 02 '14 at 06:49
  • @Yakk can you elaborate please? – norlesh Jan 02 '14 at 07:34
  • `auto x = MakeTuple(2); std::cout << std::is_same< decltype(x), std::tuple >::value << "\n";` will print `0`. – Yakk - Adam Nevraumont Jan 02 '14 at 13:23