Question

I want to create a boost fusion vector with references to variables. The goal is to pass to a function a various number of parameters and add them to the fusion vector. Because of the reference types, I add every element one at a time with TMP. But sometimes some elements in the fusion vector are wrong. It seems like undefined behavior (wrong value, read access violation).
I wrote an example, in which I 'unrolled' the recursion used in TMP for easier understanding. It simply add two references to a fusion vector and outputs the result:

#include <iostream>

#include <boost/ref.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container.hpp>

using namespace boost;
using namespace boost::fusion;

//add second element
template <typename T>
vector<int&, double&> createVector2(T vec, double& v2) {
     auto newVector = join(vec, make_vector(ref(v2)));
     return newVector;
}

//add first element
template <typename T>
vector<int&, double&> createVector(T vec, int& v1, double& v2) {
     auto newVector = join(vec, make_vector(ref(v1)));
     return createVector2(newVector, v2); 
}

int main() {
     int v1 = 10;
     double v2 = 15.3;

     vector<> vec;
     auto ret = createVector(vec, v1, v2);

     std::cout << at_c<0>(ret) << std::endl;
     std::cout << at_c<1>(ret) << std::endl;

     if (at_c<0>(ret) != v1) {
         std::cout << "FAILED" << std::endl;
     }

     if (at_c<1>(ret) != v2) {
         std::cout << "FAILED" << std::endl;
     }

     return 0;
}

The program crashes, when a reference in the boost fusion vector is accessed (read access violation), first in this line:

std::cout << at_c<0>(ret) << std::endl;

I am using VC11 as compiler (version 17.00.51106.1). The error is only in release mode. But when I use VC10, GCC 4.7.0 or GCC 4.7.2 there is no error and the program works perfectly fine.
To get the program work with VC11, I have to change this line

auto newVector = join(vec, make_vector(ref(v1)));

to

auto newVector = as_vector(join(vec, make_vector(ref(v1))));

So, does the example above contain a bug or is there something wrong with the VC11 optimizer? Is it allowed to pass a local boost fusion view (boost::fusion::join returns only a view and the view gets converted with boost::fusion::vector to a 'normal' boost::fusion::vector) to another function by value?

Was it helpful?

Solution

I submitted a bug report at Microsoft connect (link). So the answer to my question is that there is a bug in my code.

The problem is that the join function returns a boost fusion joint_view, which contains the two elements seq1 and seq2. They are defined as:

template <typename Sequence1, typename Sequence2>
struct joint_view : sequence_base<joint_view<Sequence1, Sequence2> >
{
    (...)
private:
    typename mpl::if_<traits::is_view<Sequence1>, Sequence1, Sequence1&>::type seq1;
    typename mpl::if_<traits::is_view<Sequence2>, Sequence2, Sequence2&>::type seq2;
};

The problem with my code is that I pass a temporary object (returned from make_vector) to the join function. A boost fusion vector is not a view and seq1 and seq2 are references. So the join function returns a joint_view containing a reference to a temporary object, which is invalid. There are two modifications to solve this:

  1. First solution (same for createVector):

    template <typename T>
    vector<int&, double&> createVector2(T vec, double& v2) {
        auto x = make_vector(ref(v2));
        auto newVector = join(vec, x);
        return newVector;
    }
    

    Now join returns a joint_view which contains a reference to x, which is valid. At the end, the view is converted to a boost fusion vector and the references are resolved.

  2. Second solution (same for createVector):

    template <typename T>
    vector<int&, double&> createVector2(T vec, double& v2) {
        return join(vec, make_vector(ref(v2)));
    }
    

    The lifetime of the temporary object (returned by make_vector) is the whole return statement and as in the first version, the view will be converted to a boost fusion vector and the references are again resolved.

Thanks to Eric Brumer (Microsoft) who provided these two solutions.

Conclusion: Do not pass a temporary object to the boost fusion join function (only if it is another view).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top