Code:
#include <tuple>
#include <unordered_map>
#include <vector>
#include <type_traits>
#include <cstdio>
// Compiler used: gcc version 4.6.3 on Ubuntu, via g++ Typename.C -std=c++0x
template<class C0, class C1>
class Sheet
{
public:
// Simple "spreadsheet" class with an embedded 2-tuple.
// Here std::get<0>(embed) is an std::vector<C0>, the "first column",
// and std::get<1>(embed) is an std::vector<C1>, the "second column".
std::tuple<std::vector<C0>,
std::vector<C1>> embed;
// Template function to return the std::vector::data() pointers of the
// C0 and C1 vectors in the embedded object.
// Since std::get<I>() returns an std::vector<C0 or C1>&, but we want a
// (C0*) or (C1*), we need to strip away the reference, and then use
// std::vector::value_type to get (C0) or (C1), and add a pointer.
// I have little experience with "decltype", so this is almost certainly
// where the mistake lies.
// Notice that both std::remove_reference::type and std::vector::value_type
// are typenames, but I've got only one "typename" listed here; that
// bugs me. (I did try using remove_reference_t, but couldn't figure out
// the correct syntax for its use.)
template<int I>
auto data() -> typename std::remove_reference
< decltype(std::get<I>(embed))
> ::type
::value_type *
{
return std::get<I>(embed).data();
}
// Forwarders to the data<I>() template.
// You'll see in a minute why we need these.
C0* c0data() { return data<0>(); }
C1* c1data() { return data<1>(); }
};
template<class Key, class C0, class C1>
class UnorderedMap
{
// Now, a class with an embedded unordered_map of Keys to Sheet<C0,C1>s.
public:
std::unordered_map<Key, Sheet<C0, C1>> uomap;
// These forwarders take a Key argument, and return the data() pointers of
// the corresponding Sheet's std::vectors in the embedded unordered_map.
// PROBLEM pops up at this point:
// These *only* work when the Sheet::c0data/c1data forwarders are used.
// Compilation fails if you try to use Sheet::data<I>, with this error:
// "69:34: error: expected primary-expression before ‘)’ token".
// Since the only thing Sheet::c0data/c1data do is to call Sheet::data<I>,
// I'm at a loss to explain this.
C0* c0val(Key key)
{
//return uomap.at(key).data<0>(); // Compile time error
return uomap.at(key).c0data();
}
C1* c1val(Key key)
{
//return uomap.at(key).data<1>(); // Compile time error
return uomap.at(key).c1data();
}
};
// Test program.
int main()
{
// Create a simple three-row Sheet<float, double>.
Sheet<float,double> s;
std::get<0>(s.embed) = std::vector<float> {1., 3., 5.};
std::get<1>(s.embed) = std::vector<double> {2., 4., 6.};
// Print out the addresses of the embedded float and double vectors,
// and their first elements (1. and 2.).
printf("In original Sheet:\n");
printf("Vectors are at %p and %p\n", s.data<0>(), s.data<1>());
printf("First values: %f and %f\n", s.data<0>()[0], s.data<1>()[0]);
// Now make up an UnorderedMap whose embedded object contains only one
// element: the above-defined Sheet, at integer Key value 3.
UnorderedMap<int, float, double> x;
x.uomap[3] = s;
// Here in main(), there is no problem with using Sheet::data<I>.
printf("In UnorderedMap:\n");
printf("Vectors are at %p and %p\n", x.uomap[3].data<0>(),
x.uomap[3].data<1>());
printf("First values: %f and %f\n", x.uomap[3].data<0>()[0],
x.uomap[3].data<1>()[0]);
printf("Second values: %f and %f\n", x.c0val(3)[1],
x.c1val(3)[1]);
}
Thanks