Thread: Boost tuple and Vector problem

  1. #1
    Registered User
    Join Date
    Sep 2009
    Posts
    7

    Boost tuple and Vector problem

    Hi everyone!

    I'm doing a test to try and improve my understanding of vectors and boost tuples and am having the following problem.

    I have simplified the issue down to the code pasted below and am working with:
    - MS Visual C++ 2008
    - Boost library 1.38

    Aim
    - I want to create a vector of tuple<int, double> elements and pass it by reference to a method/template to populate it.

    At the moment in the populate method I can create the tuple element and insert it into a vector also created in the method, but I cannot insert the tuple into the vector which has been passed to the method by reference.

    Notes

    - I cannot change any of the code in main(), I can only write code in the populateData method.
    - Not other fancy libraries etc

    Error

    - The error occurs in the tuple_basic.hpp and I have included it below (cut the length of it but important info is there)

    Any help or info would be appreciated!!

    Thanks!!!

    Code Start============================================= ==================
    Code:
    #include "stdafx.h"
    #include <vector>
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <numeric>
    
    
    #include <boost/tuple/tuple.hpp>
    #include <boost/tuple/tuple_io.hpp>
    
    
    template <typename CONTAINER>
    void populateData(CONTAINER& container, size_t count)
    {
    
    	typedef boost::tuple<int, double> row1_t;
    
    	std::vector<row1_t> myVect;
    
    	for(size_t i=0 ; i<count ; i++)
    	{
    		intTuple tempTuple((int(i)));
    		myVect.push_back(tempTuple);
    	
    		row1_t myTuple((int(i)), (double(i))/10);
    
    		myVect.push_back(myTuple);	//This works!
    		container.push_back(myTuple);	//THIS IS THE PROBLEM LINE
    	};
    };
    
    
    int main()	
    {
    	typedef boost::tuple<int, double>	row1_t;
    	typedef std::vector<row1_t>						vec1_t;
    	vec1_t vec1;
    	populateData(vec1, 100);
    	TEST_REQUIRE_EQUAL(vec1.size(), 100);
    	TEST_CHECK_EQUAL_AS_STR(vec1[1],"(1 0.1)");
    	TEST_CHECK_EQUAL_AS_STR(vec1[99],"(99 9.9)");
    
    
    }
    Code End=============================================== =================


    Full Error:

    1>------ Build started: Project: wintonExpertTest, Configuration: Debug Win32 ------
    1>Compiling...
    1>wintonExpertTest.cpp
    1>c:\program files\boost\boost_1_38\boost\tuple\detail\tuple_ba sic.hpp(373) : error C2039: 'tail' : is not a member of 'boost::tuples::cons<HT,TT>'
    1> with
    1> [
    1> HT=double,
    1> TT=boost::tuples::detail::map_tuple_to_cons<boost: :tuples::null_type,boost::tuples::null_type,boost: :tuples::null_type,boost::tuples::null_type,boost: :tuples::null_type,boost::tuples::null_type,boost: :tuples::null_type,boost::tuples::null_type,boost: :tuples::null_type,boost::tuples::null_type>::type
    1> ]
    1> c:\program files\boost\boost_1_38\boost\tuple\detail\tuple_ba sic.hpp(373) : see reference to function template instantiation 'boost::tuples::cons<HT,TT>::cons<T0,boost::tuples ::detail::map_tuple_to_cons<boost::tuples::null_ty pe,boost::tuples::null_type,boost::tuples::null_ty pe,boost::tuples::null_type,boost::tuples::null_ty pe,boost::tuples::null_type,boost::tuples::null_ty pe,boost::tuples::null_type,boost::tuples::null_ty pe,boost::tuples::null_type>::type>(const boost::tuples::cons<HT,boost::tuples::detail::map_ tuple_to_cons<boost::tuples::null_type,boost::tupl es::null_type,boost::tuples::null_type,boost::tupl es::null_type,boost::tuples::null_type,boost::tupl es::null_type,boost::tuples::null_type,boost::tupl es::null_type,boost::tuples::null_type,boost::tupl es::null_type>::type> &)' being compiled
    1> with
    1> [
    1> HT=double,
    1> TT=boost::tuples::cons<double,boost::tuples::detai l::map_tuple_to_cons<boost::tuples::null_type,boos t::tuples::null_type,boost::tuples::null_type,boos t::tuples::null_type,boost::tuples::null_type,boos t::tuples::null_type,boost::tuples::null_type,boos t::tuples::null_type,boost::tuples::null_type,boos t::tuples::null_type>::type>,
    1> T0=double
    1> ]

  2. #2
    Ex scientia vera
    Join Date
    Sep 2007
    Posts
    477
    1. Why not declare the row1_t typedef at the top of the file, right below the include directives?
    2. Your problem probably lies in the fact that CONTAINER could be any type. You could try declaring your function as follows:

    Code:
    template <typename elemType>
    void populateData(vector<elemType> container, size_t count)
    {
          // .... 
    }
    If what you are trying to do were possible, what would happen if you passed it a container that doesn't have a member function called "push_back()" ?

    If this isn't the issue, or what you are doing is supposed to be possible, then I'll have to step down and leave this to someone wiser than I.
    "What's up, Doc?"
    "'Up' is a relative concept. It has no intrinsic value."

  3. #3
    Registered User
    Join Date
    Sep 2009
    Posts
    7
    It's part of a test that I found that I wanted to work through to learn more about templates/tuples and vectors etc.

    I've included the full test file which will hopefully make things a little clearer.

    Any hints on how to approach the problem would be great!

    Thanks again!

  4. #4
    Ex scientia vera
    Join Date
    Sep 2007
    Posts
    477
    Actually, ignore my last post. I am still polishing my C++-fu myself.

    The very point of templates is being able to provide type-independent functions. What you are doing wrong can be narrowed to a single line:

    Code:
    int main()	
    {
    	typedef boost::tuple<int, double>	row1_t;
    	typedef std::vector<row1_t>						vec1_t;
    	vec1_t vec1;
    	populateData(vec1, 100);
    	TEST_REQUIRE_EQUAL(vec1.size(), 100);
    	TEST_CHECK_EQUAL_AS_STR(vec1[1],"(1 0.1)");
    	TEST_CHECK_EQUAL_AS_STR(vec1[99],"(99 9.9)");
    
    
    }
    If you are able to do that, what I said before is valid. If the compiler doesn't know what type the function is using, how can it be sure that it has a member function called push_back?

    You need to call your function as follows:

    Code:
    #include <iostream>
    #include <vector>
    
    #include <boost/tuple/tuple.hpp>
    
    using namespace std;
    using namespace boost;
    
    typedef tuple<int, int> myTupleType;
    typedef vector<myTupleType> myVectorType;
    
    
    template <typename CONTAINER>
    void populateData(CONTAINER &container, size_t count)
    {
        for(size_t i = 0; i < count; i++)
            container.push_back(myTupleType(i, i));
    }
    
    int main(void)
    {
        myVectorType v;
    
        populateData<myVectorType>(v, 20);
    
        for(int i = 0; i < v.size(); i++)
            cout << static_cast<int>(v[i].get_head()) << endl;
    
        cin.get();
        return 0;
    }
    Note that final for loop is simply to be sure that the vector was actually populated.
    "What's up, Doc?"
    "'Up' is a relative concept. It has no intrinsic value."

  5. #5
    Registered User
    Join Date
    Sep 2009
    Posts
    7
    thanks for the reply...

    It's part of a larger test/problem and I've posted the complete file above...

    As such I don't think that I can modify the main()...

    And re; the push_back comment, I guess I should try to use functions that are common to all container classes such as insert...

    I'm really looking for ideas on how to approach this whole problem too as the second part of it does the same but with a three element tuple...

    eek!

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    My understanding was that any template parameter that can be deduced from the arguments can be omitted: since the template name is the type of the first variable, then using
    Code:
    populateData(vec1, 100);
    is automatically a call to
    Code:
    populateData<typeof vec1>(vec1, 100);
    If that wasn't true, we'd get a "I don't know that function" error, instead of some weird error in the guts of the function itself.

    I don't have Boost anywhere to hand, so I can't really test out what's going on. One thing I would suggest is to see what typeof container is, or at least see whether typeof myVect==typeof container. What worries me is the double typedef of your row1_t type -- C++ really isn't very happy about that sort of thing. Typedef it once early and maybe that will help.

  7. #7
    Registered User
    Join Date
    Sep 2009
    Posts
    7
    Yeah, I wasn't sure what to do about the typedef, I don't want to remove it from main() but I don't really want to do it twice... I thought i'd concentrate on the other parts of the problem and figure that out later....

    I'll have a look at typeof, thanks

  8. #8
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by LuckyPierre View Post
    Yeah, I wasn't sure what to do about the typedef, I don't want to remove it from main() but I don't really want to do it twice... I thought i'd concentrate on the other parts of the problem and figure that out later....
    That's something you want to rethink. Typedefs inside main are legal, but are never correct.

    (*It is possible there is a situation in which they are correct, but I am unable to come up with one.)

  9. #9
    The larch
    Join Date
    May 2006
    Posts
    3,573
    If I'm not mistaken, typedefs can have scope, so this shouldn't be a problem.

    With the implementation you posted initially, I can't reproduce that error: I get that intTuple is undefined. (That code doesn't really make sense.)

    However, from your test code it appears that populateData must support different kinds of vectors and tuples, and do a different thing each time:

    Code:
    typedef boost::tuple<int, double> row1_t;
    TEST_CHECK_EQUAL_AS_STR(vec1[1],"(1 0.1)");
    
    typedef boost::tuple<int, double, double> row2_t;
    TEST_CHECK_EQUAL_AS_STR(vec2[1],"(1 0.1 0.01)");
    
    typedef boost::tuple<int, double, std::string> row3_t;
    TEST_CHECK_EQUAL_AS_STR(vec3[1],"(1 0.1 1/100)");
    This I believe is the reason why your attempt failed with the passed-in vector, but not with local vector<tuple<int, double> >: the second and third vector hold a different type of tuple.

    It seems to me that some specialization/overload is needed (at least one for double and one for string).

    The general pattern seems to be that each member of the tuple stores the previous value divided by 10.

    You could create and use three overloads that create each of the three tuples:

    Code:
    void fill_tuple(int n, boost::tuple<int, double>&);
    void fill_tuple(int n, boost::tuple<int, double, double>&);
    void fill_tuple(int n, boost::tuple<int, double, std::string>&);
    And if you need to know the type that the container holds, you would typedef it like this:

    Code:
    typedef typename Container::value_type tuple_type;
    I suppose the real aim of the exercise is to loop over the tuple with compile-time recursion, so as to fill it out with a suitable values. And so it would also support arbitrary test cases like:

    Code:
    typedef boost::tuple<int, std::string, double, std::string, double>	row5_t;
    TEST_CHECK_EQUAL_AS_STR(vec3[1],"(1 1/10 0.01 1/1000 0.00001)");
    At least that's what the 3rd challenge says: create a generalized view.
    Last edited by anon; 09-24-2009 at 07:01 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Yep, this is a compile-time meta-programming challenge. You're basically going about it with too naive an approach.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #11
    Registered User
    Join Date
    Sep 2009
    Posts
    7
    Quote Originally Posted by CornedBee View Post
    Yep, this is a compile-time meta-programming challenge. You're basically going about it with too naive an approach.
    Thanks for the comment, as you can see I haven't been too sure how to approach this problem so thanks for the pointer...

    Could you give any more info on what I should be looking at to learn about this stuff, "compile time meta programming" is helpful be as you obviously know more about how to approach this problem than me any more info (the more specific the better!) would be very very helpful!


    Thanks for your help!


  12. #12
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I recommend you buy the book "C++ Template Metapogramming" by Abrahams and Gurtovoy, it's pretty much the best resource on the topic. Or if you want a somewhat gentler introduction, "Modern C++ Design" by Alexandrescu is a very significant work on this sector.

    Explaining metaprogramming otherwise is quite hard, and I'm not aware of any tutorial on the subject.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  13. #13
    The larch
    Join Date
    May 2006
    Posts
    3,573
    I don't think it is all that hard. Can you calculate a factorial of a number with template metaprogramming? What you should do is in principle the same thing - a template class with a method instantiating other instances of the template to access each item in the tuple recursively. Perhaps the only missing clue is how you know when to stop the recursion, but you can find out the number of items in a tuple at compile time with

    Code:
    boost::tuples::length<SomeTuple>::value
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  14. #14
    Registered User
    Join Date
    Sep 2009
    Posts
    7
    Firstly, thanks for everyone's help so far! So here's my code now.

    Code:
    #include "stdafx.h"
    #include <vector>
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <numeric>
    
    #include <boost/lexical_cast.hpp>	//MR Added to cast from double to string
    
    #include <boost/tuple/tuple.hpp>
    #include <boost/tuple/tuple_io.hpp>
    
    
    static bool testFailure(false);
    static size_t testCount(0);
    
    
    #define TEST_REQUIRE_EQUAL(a, b)													\
    {																					\
    	++testCount;																	\
    	if (a != b) {																	\
    		std::cout << "ERROR: TEST ABORTING (" #a " != " #b ")" << std::endl;		\
    		exit(1);																	\
    	}																				\
    }
    
    #define TEST_CHECK_EQUAL(a, b)														\
    {																					\
    	++testCount;																	\
    	if (a != b) {																	\
    		std::cout << "ERROR: TEST FAILURE (" #a " != " #b ")" << std::endl;			\
    		testFailure=true;															\
    	}																				\
    }
    
    #define TEST_CHECK_EQUAL_AS_STR(a, b)												\
    {																					\
    	++testCount;																	\
    	std::stringstream lhs, rhs;														\
    	lhs << a;																		\
    	rhs << b;																		\
    	if (lhs.str() != rhs.str()) {													\
    		std::cout << "ERROR: TEST FAILURE ("										\
    			<< lhs.str()															\
    			<< " != "																\
    			<< rhs.str()															\
    			<< ")"																	\
    			<< std::endl															\
    		;																			\
    		testFailure=true;															\
    	}																				\
    }
    
    //Function for challenge 1, for when recursion hits last tuple element
    inline void set_to_val_over_ten(const boost::tuples::null_type&, double){};
    
    //Challenge 1 - populates tuple recursively
    template<typename H, typename T>
    inline void set_to_val_over_ten(boost::tuples::cons<H, T>& x, double V )
    {
    	x.get_head() = V;
    	set_to_val_over_ten(x.get_tail(), V/10);
    }
    
    //Challenge 2 - specialisation for strings
    template< typename T>
    inline void set_to_val_over_ten(boost::tuples::cons<std::string, T>& x, double V )
    {
    
    	x.get_head() = boost::lexical_cast<std::string>(V);
    	set_to_val_over_ten(x.get_tail(), V/10);
    }
    
    //Challenge 1 - Provided function template
    template <typename CONTAINER>
    void populateData(CONTAINER& container, size_t count)
    {
    	for(size_t i=0 ; i<count ; i++)
    	{
    		typename CONTAINER::value_type tupleToAdd(i);
    		set_to_val_over_ten(tupleToAdd.get_tail(), double(i)/10.0);
    		container.push_back(tupleToAdd);	
    	};
    }
    
    
    
    
    template<typename ITERATOR_TYPE>
    typename ITERATOR_TYPE::value_type sum(const ITERATOR_TYPE& first, const ITERATOR_TYPE& last)
    {
    	typedef typename ITERATOR_TYPE::value_type value_type;
    	value_type count(static_cast<value_type>(std::distance(first,last)));
    	if(count==0)
    		throw std::exception("Cannot calculate the sum of empty series.");
    	return std::accumulate(first, last, value_type(), std::plus<value_type>());
    }
    
    
    template<typename ITERATOR_TYPE>
    typename ITERATOR_TYPE::value_type mean(const ITERATOR_TYPE& first, const ITERATOR_TYPE& last)
    {
    	typedef typename ITERATOR_TYPE::value_type value_type;
    	value_type count(static_cast<value_type>(std::distance(first,last)));
    	return sum(first, last)/count;
    }
    
    template<typename CONTAINER> 
    typename CONTAINER::value_type mean(const CONTAINER& container)							
    {
    	return mean(container.begin(), container.end());
    }
    
    
    int main()	
    {
    	// challenge 1 - pass the following tests
    	typedef boost::tuple<int, double>				row1_t;
    	typedef std::vector<row1_t>						vec1_t;
    	vec1_t vec1;
    	populateData(vec1, 100);
    	TEST_REQUIRE_EQUAL(vec1.size(), 100);
    	TEST_CHECK_EQUAL_AS_STR(vec1[1],"(1 0.1)");
    	TEST_CHECK_EQUAL_AS_STR(vec1[99],"(99 9.9)");
    
    
    	typedef boost::tuple<int, double, double>		row2_t;
    	typedef std::vector<row2_t>						vec2_t;
    	vec2_t vec2;
    	populateData(vec2, 100);
    	TEST_REQUIRE_EQUAL(vec2.size(), 100);
    	TEST_CHECK_EQUAL_AS_STR(vec2[1],"(1 0.1 0.01)");
    	TEST_CHECK_EQUAL_AS_STR(vec2[99],"(99 9.9 0.99)");
    
    
    	// challenge 2 - create a specialisation for strings
    	typedef boost::tuple<int, double, std::string>	row3_t;
    	typedef std::vector<row3_t>						vec3_t;
    	vec3_t vec3;
    	populateData(vec3, 100);
    	TEST_REQUIRE_EQUAL(vec3.size(), 100);
    	TEST_CHECK_EQUAL_AS_STR(vec3[1],"(1 0.1 1/100)");
    	TEST_CHECK_EQUAL_AS_STR(vec3[99],"(99 9.9 99/100)");
    
    	// challenge 3 - create a generalised view on the series - ideal solution should not copy data
    	//TEST_CHECK_EQUAL_AS_STR( mean(columnViewOfTuple<0>(vec1)), 49);
    	//TEST_CHECK_EQUAL_AS_STR( mean(columnViewOfTuple<1>(vec1)), 4.95);
    
    	//TEST_CHECK_EQUAL_AS_STR( mean(columnViewOfTuple<0>(vec2)), 49);
    	//TEST_CHECK_EQUAL_AS_STR( mean(columnViewOfTuple<1>(vec2)), 4.95);
    	//TEST_CHECK_EQUAL_AS_STR( mean(columnViewOfTuple<2>(vec2)), 0.495);
    
    	//TEST_CHECK_EQUAL_AS_STR( mean(columnViewOfTuple<0>(vec3)), 49);
    	//TEST_CHECK_EQUAL_AS_STR( mean(columnViewOfTuple<1>(vec3)), 4.95);
    
    	TEST_CHECK_EQUAL_AS_STR(testCount, 17);
    	if (!testFailure) {
    		std::cout << "Congratulations!\n";
    	}
    
    }

    The problem I'm now having is when main() calls the following line in challenge 2

    Code:
    TEST_CHECK_EQUAL_AS_STR(vec3[1],"(1 0.1 1/100)");
    I've made the mistake of just changing the double to a string instead of a fraction such as "1/100" by using the boost lexical cast.

    While I could change the template function to unclude the vector element number currently being iterated
    (In the above case this would give the numerator) and also pass in the tuple element number being
    iterated through (10^ this number would give the denominator).

    I'm fairly sure there must be a more elegant way of doing this?

    Any ideas please?

  15. #15
    The larch
    Join Date
    May 2006
    Posts
    3,573
    It seemed to have a problem selecting the string overload for me.

    What you might do is pass to integers, nominator and denominator, multiplying the latter by 10 for each recursive call.

    You could also break out and overload the part that actually sets the value of the tuple item. All in all:

    Code:
    template <class T>
    void set_number(T& t, size_t nom, size_t denom);
    
    void set_number(std::string& s, size_t nom, size_t denom);
    
    inline void set_to_val_over_ten(const boost::tuples::null_type&, size_t, size_t);
    
    template<typename H, typename T>
    inline void set_to_val_over_ten(boost::tuples::cons<H, T>& x, size_t nom, size_t denom)
    {
    	set_number(x.get_head(), nom, denom);
    	set_to_val_over_ten(x.get_tail(), nom, denom * 10);
    }
    
    //Challenge 1 - Provided function template
    template <typename CONTAINER>
    void populateData(CONTAINER& container, size_t count)
    {
    	for(size_t i=0 ; i<count ; i++)
    	{
    		typename CONTAINER::value_type tupleToAdd(i);
    		set_to_val_over_ten(tupleToAdd.get_tail(), i, 10);
    		container.push_back(tupleToAdd);
    	};
    }
    Now, I wonder what happens if the first member of the tuple were a string?
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

Popular pages Recent additions subscribe to a feed

Tags for this Thread