![]() |
| | #1 |
| Registered User Join Date: Sep 2009
Posts: 7
| Boost tuple and Vector problem 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)");
}
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> ] |
| LuckyPierre is offline | |
| | #2 |
| Ex scientia vera Join Date: Sep 2007
Posts: 426
| 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 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." |
| IceDane is offline | |
| | #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! |
| LuckyPierre is offline | |
| | #4 |
| Ex scientia vera Join Date: Sep 2007
Posts: 426
| 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)");
}
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;
}
__________________ "What's up, Doc?" "'Up' is a relative concept. It has no intrinsic value." |
| IceDane is offline | |
| | #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! |
| LuckyPierre is offline | |
| | #6 |
| and the Hat of Guessing Join Date: Nov 2007
Posts: 8,740
| 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); Code: populateData<typeof vec1>(vec1, 100); 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. |
| tabstop is offline | |
| | #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 |
| LuckyPierre is offline | |
| | #8 | |
| and the Hat of Guessing Join Date: Nov 2007
Posts: 8,740
| Quote:
(*It is possible there is a situation in which they are correct, but I am unable to come up with one.) | |
| tabstop is offline | |
| | #9 | |
| The larch Join Date: May 2006
Posts: 3,082
| 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)"); 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>&); Code: typedef typename Container::value_type tuple_type; 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)");
__________________ I might be wrong. Quote:
Last edited by anon; 09-24-2009 at 07:01 AM. | |
| anon is offline | |
| | #10 |
| Cat without Hat Join Date: Apr 2003
Posts: 8,439
| 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 |
| CornedBee is offline | |
| | #11 | |
| Registered User Join Date: Sep 2009
Posts: 7
| Quote:
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! | |
| LuckyPierre is offline | |
| | #12 |
| Cat without Hat Join Date: Apr 2003
Posts: 8,439
| 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 |
| CornedBee is offline | |
| | #13 | |
| The larch Join Date: May 2006
Posts: 3,082
| 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. Quote:
| |
| anon is offline | |
| | #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)"); 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? |
| LuckyPierre is offline | |
| | #15 | |
| The larch Join Date: May 2006
Posts: 3,082
| 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);
};
}
__________________ I might be wrong. Quote:
| |
| anon is offline | |
![]() |
| Tags |
| boost, c++, error, tuple, vector |
| Thread Tools | |
| Display Modes | |
|