|
Boost Users : |
Subject: Re: [Boost-users] [BGL] Unable to sort list of edge_descriptors
From: Peter Wright (pete_at_[hidden])
Date: 2010-11-07 01:20:50
Hi Mike,
This problem intrigued me enough that I felt like actually doing the
work to make sense of it - which turned out to be a useful learning
experience, as I now:
(a) better understand the typename keyword, and
(b) realise how amazingly helpful the Clang C++ compiler output can be. :)
Short explanation - I think you did at least three things wrong:
1. you used the "typename" keyword incorrectly,
2. you defined the SortByName functor class incorrectly (inheriting
from std::binary_function when the operator() was _not_ a binary
function), then
3. you *used* the SortByName functor incorrectly (by supplying it to
std::list::sort, which expects a 2-arg function instead of 3-arg).
Longer explanation below:
On 06/11 02:04:57, Mike Douglass wrote:
> I'm trying to sort a list of edge_descriptors, but I get the
> compiler error described below.
[ ... ]
> //With the above line uncommented the compiler says:
>
> /*
> ./Includes/Utilities.cpp: In function ???void Sort_Test(Graph&)???:
> ./Includes/Utilities.cpp:539: error: wrong number of template arguments (1,
> should be 2)
> ./Includes/Utilities.cpp:513: error: provided for ???template<class Graph, class
> edgeDescriptor> struct SortByName???
> make: *** [plc] Error 1
> */
[ ... ]
> But clearly there are two template arguments - < typename Graph,
> typename edgeDescriptor >
(Note: I rewrote your code into a bastardised form that doesn't use
Boost::Graph, as the problem doesn't look like it's got anything to do
with that.)
Compile the attached douglass-broken.cpp with GNU g++ and you get
error output closely matching your report (I've replaced GCC's open/close
single-quotes with ' to avoid encoding issues):
----------------------------------------------------------------------
$ g++ douglass-broken.cpp -o douglas-broken
douglass-broken.cpp: In function 'void Sort_Test(Graph&)':
douglass-broken.cpp:45:62: error: wrong number of template arguments (1, should be 2)
douglass-broken.cpp:21:8: error: provided for 'template<class Graph, class edgeDescriptor> struct SortByName'
$
----------------------------------------------------------------------
I agree, it's not a particuarly helpful error message - in fact it's
outright misleading.
The output from clang++, however, is *extremely* helpful (though it's
much easier to read with the default coloured/bolded output, here it's
just plain text):
----------------------------------------------------------------------
$ clang++ douglass-broken.cpp -o douglass-broken
douglass-broken.cpp:45:32: error: expected a qualified name after 'typename'
A.sort(SortByName<typename Graph, typename edgeDescriptor>());
^
douglass-broken.cpp:45:48: error: expected a qualified name after 'typename'
A.sort(SortByName<typename Graph, typename edgeDescriptor>());
^
In file included from douglass-broken.cpp:2:
In file included from /usr/include/c++/4.5.1/list:65:
/usr/include/c++/4.5.1/bits/list.tcc:286:12: error: no matching function for call to object of type
'SortByName<std::map<std::basic_string<char>, Edge, std::less<std::basic_string<char> >,
std::allocator<std::pair<std::basic_string<char> const, Edge> > >, std::basic_string<char> >'
if (__comp(*__first2, *__first1))
^~~~~~
/usr/include/c++/4.5.1/bits/list.tcc:398:7: note: in instantiation of function template specialization 'std::list<std::basic_string<char>,
std::allocator<std::basic_string<char> > >::merge<SortByName<std::map<std::basic_string<char>, Edge,
std::less<std::basic_string<char> >, std::allocator<std::pair<std::basic_string<char> const, Edge> > >, std::basic_string<char> > >'
requested here
__counter->merge(__carry, __comp);
^
douglass-broken.cpp:45:5: note: in instantiation of function template specialization 'std::list<std::basic_string<char>,
std::allocator<std::basic_string<char> > >::sort<SortByName<std::map<std::basic_string<char>, Edge, std::less<std::basic_string<char>
>, std::allocator<std::pair<std::basic_string<char> const, Edge> > >, std::basic_string<char> > >' requested here
A.sort(SortByName<typename Graph, typename edgeDescriptor>());
^
douglass-broken.cpp:64:5: note: in instantiation of function template specialization 'Sort_Test<std::map<std::basic_string<char>, Edge,
std::less<std::basic_string<char> >, std::allocator<std::pair<std::basic_string<char> const, Edge> > > >' requested here
Sort_Test<Graph>(the_graph);
^
douglass-broken.cpp:23:10: note: candidate function not viable: requires 3 arguments, but 2 were provided
bool operator()(const Graph& g, const edgeDescriptor& a, const edgeDescriptor& b)
^
3 errors generated.
----------------------------------------------------------------------
So, the first problem is that you used typename where it shouldn't be
used, in this line:
A.sort(SortByName<typename Graph, typename edgeDescriptor>());
...and I suspect that confused the g++ parser so that it interpreted
the expression "typename Graph, typename edgeDescriptor" as a *single*
type, resulting in the confusing error message.
You can verify this by adding extra nonsense types in the same way, eg.:
A.sort(SortByName<typename Graph, typename edgeDescriptor, typename NonexistentType, typename Rubbish, typename Bollocks>());
Compile that and the error message supplied by my g++ (I'm using
version 4.5.1) is still the same "wrong number of template arguments
(1, should be 2)", despite it now looking like *five* arguments :).
As you can see, the Clang C++ compiler does a much better job, both
pointing out the typename-usage error *and* reporting (albeit
indirectly) that you're supplying a three-argument functor to the
std::list::sort, when it actually requires a binary functor/function.
(Note: To be fair to GCC, once you remove the two incorrect "typename"
keywords in the A.sort line, it also generates a usable error message.)
One way to fix it would be as Cedric suggested, and that's the model I
used in my douglass-working.cpp (attached). I changed SortByName so it
requires a const Graph& in its constructor, stores a reference to that
in a private variable, *then* changed SortByName::operator() to take
two edgeDescriptor args and return bool.
It is dicey in one way, in that it stores a *reference* to its Graph -
so if the SortByName object outlives the Graph then it could blow up
spectacularly. But the alternative of copying the entire graph seemed
a bit excessive.
Another way to fix it (and also avoid the reference-storing problem)
requires using Boost::Bind - change SortByName to this:
----------------------------------------------------------------------
template <typename G, typename ED>
struct SortByName
{
typedef bool result_type; // required for use by STL algorithms.
result_type operator()(G& g, const ED& a, const ED& b)
{
return g[a].eName < g[b].eName;
}
};
----------------------------------------------------------------------
...and the A.sort call to this:
A.sort( boost::bind(SortByName<Graph, edgeDescriptor>(), g, _1, _2) );
That seems to work fine, and may be a closer match to what you
originally intended.
Pete.
-- "DAMN WHO MESSED WITH MY CAPSLOCK KEY that's better." -- geoff lane, <e886430646_at_[hidden]>, asr
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net