removing strings from a vector via boost::bind - c++

I am trying to remove short strings from a vector.
std::vector<std::string> vec;
// ...
vec.erase(std::remove_if(vec.begin(),
vec.end(),
boost::bind(std::less<size_t>(),
boost::bind(&std::string::length, _1),
5),
vec.end());
The compiler spits out a very large error message:
qwer.cpp:20: error: no matching function for call to 'remove_if(__gnu_cxx::__nor
mal_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char
> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator
<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::al
locator<char> > > > >, __gnu_cxx::__normal_iterator<std::basic_string<char, std:
:char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char,
std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_strin
g<char, std::char_traits<char>, std::allocator<char> > > > >, boost::_bi::bind_t
<boost::_bi::unspecified, std::less<unsigned int>, boost::_bi::list2<boost::_bi:
:bind_t<unsigned int, boost::_mfi::cmf0<unsigned int, std::basic_string<char, st
d::char_traits<char>, std::allocator<char> > >, boost::_bi::list1<boost::arg<1>
> >, boost::_bi::value<int> > >, __gnu_cxx::__normal_iterator<std::basic_string<
char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_st
ring<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::b
asic_string<char, std::char_traits<char>, std::allocator<char> > > > >)'
The following solution works:
vec.erase(std::remove_if(vec.begin(),
vec.end(),
boost::bind(&std::string::length, _1) < 5),
vec.end());
But I am still curious as to what I did wrong in the first version. Thanks!

It looks like you got your parenthesis off (there should be two after 5, one to close the bind, one to close the remove_if.) I am surprised this didn't give another error message about invalid token or something though, as the parens are clearly unbalanced (did you remove an extra close paren from the end while preparing for SO?). It looks like this is the case, because if you read the template arguments to remove_if in the error message, the next to last one is a boost bind_t, followed by another gnu::iterator.

Related

Boost.Python - how do you convert a std::map<std::string, std::string> to a dict[str, str]?

I have a Boost.python .cpp that looks like this
BOOST_PYTHON_MODULE(my_module)
{
class_<MyCustomClass>("MyCustomClass")
.def("getDict",
&MyCustomClass::getDict, // This returns std::map<std::string, std::string>
bp::return_value_policy<bp::return_by_value>()
;
}
When I try to call MyCustomClass().getDict(), I get this error:
TypeError: No to_python (by-value) converter found for C++ type: std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >
It looks like Boost doesn't attempt to make this mapping for you by default. How do you let Boost know that it's okay to make that conversion?
At the moment I'm getting around the issue by having MyCustomClass::getDict return a boost::python::dict but, because this API is meant to be used by both C++ and Python users, it'd be much preferred to return the native std::map<std::string, std::string> for the C++ API and do a proper convert in the Python bindings.
I tried adding .def(map_indexing_suite<std::map<std::string, std::string> >()) to the class_ definition but it had no effect. I still get the same TypeError, as shown above.

How can I suppress a stack-buffer-overflow from AddressSanitizer in gcc

My app is using boost::program_options and it's triggering an AddressSanitizer "stack-buffer-overflow" while generating an error message from an exception.
I'm not worried about the boost bug - the functionality works and this is just in the command line parsing portion of a non-production app. However I'd like to suppress the AddressSanitizer message.
ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffe6ce7070 at pc 0x0000007406cd bp 0x7fffe6ce6fe0 sp 0x7fffe6ce6fd8
READ of size 8 at 0x7fffe6ce7070 thread T0
#0 0x7406cc in std::_Head_base<0ul, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, false>::_M_head(std::_Head_base<0ul, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, false>&) /frc/toolchain6/include/c++/5.3.0/tuple:142
#1 0x7406cc in _M_create_node /frc/toolchain6/include/c++/5.3.0/tuple:347
#2 0x7403fd in std::_Rb_tree_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::_M_emplace_hint_unique<std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<> >(std::_Rb_tree_const_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>&&, std::tuple<>&&) /frc/toolchain6/include/c++/5.3.0/bits/stl_tree.h:2170
#3 0xd5eff8 in boost::program_options::error_with_option_name::substitute_placeholders(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const (/home/joe/myapp_workspace/myapp/myapp-debug+0xd5eff8)
#4 0xd5c0dd in boost::program_options::error_with_option_name::what() const (/home/joe/myapp_workspace/myapp/myapp-debug+0xd5c0dd)
#5 0x58addf in main /home/joe/myapp_workspace/myapp/main.cpp:62
#6 0x7fd7e056176c in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2176c)
#7 0x436aa0 (/home/joe/myapp_workspace/myapp/myapp-debug+0x436aa0)
I've tried using the ASAN_OPTIONS suppression file method but that only seems to support a very short list of error types (such as "vptr_check" and "leak").
I don't think there's an easy way to suppress this error - Clang version of Asan has blacklisting mechanism but it's context insensitive so you'd have to disable memory checking in all usages of std::string which is highly undesirable.
One option is to use -fsanitize-recover=address compiler flag and add halt_on_error=0 to your ASAN_OPTIONS environment variable (see wiki for details and note that recovery is only supported is relatively new GCC and Clang). This will continue execution after first error. You'll then be able to examine full Asan report and select what interests you.

memory corruption due to #pragma pack error - std map corruption- crashing on insert

I have a project I am working on where I have some strange behavior with the std maps.
I had my own typedef map defined which mapped strings to a pointer of a custom type. The application crashed anytime that I excess the map after I add the first pair to the map.
After a lot of messing around I changed the map to a and moved it to the first call in my application and it still crashes. I have no idea what could be going on. Any help would be appreciated.
Here is the code that crashes at the moment.
LoggerPtr syslogger(Logger::getLogger("CISInterface"));
int main(int argc, char *argv[])
{
typedef std::map<string, string> MyMapDef;
MyMapDef tmpString;
tmpString.insert(MyMapDef::value_type("0000", "d"));
tmpString.insert(MyMapDef::value_type("1111", "d")); //Crashes here.
tmpString.insert(MyMapDef::value_type("2222", "d"));
// std::string configFile;
// int c;
// if(argc < 2)
// {
// //Must have c option
// std::cout << "Usage -c configFileName" << std::endl;
// exit(EXIT_FAILURE);
// }
//Rest of main commented out.
...
And here is the stack trace -
CISInterface Debug [C/C++ Application]
gdb/mi (10/31/12 6:02 PM) (Suspended)
Thread [1] (Suspended: Signal 'SIGSEGV' received. Description: Segmentation fault.)
6 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::compare(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const() 0x00000032fd49c416
5 std::operator< <char, std::char_traits<char>, std::allocator<char> >() basic_string.h:2317 0x0000000000417ec7
4 std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::operator() stl_function.h:230 0x000000000041706f
3 std::_Rb_tree<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::_Select1st<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::_M_insert_unique() stl_tree.h:1170 0x0000000000415d00
2 std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::insert() stl_map.h:500 0x00000000004150eb
1 main() CISInterface.cpp:29 0x000000000041916d
gdb (10/31/12 6:02 PM)
/home/cillian/workspace/CISInterface/Debug/CISInterface (10/31/12 6:02 PM)
What other areas should I be looking at that could be causing problems. Could it be in the libraries that I'm linking with? I have created a second project with just these lines of code that links with the same libraries (but doesn't have any code that calls into them.) and it doesn't crash.
Problem solved.
Thought I'd add it here on the off chance anyone else ever does the same thing.
I slowly removed files in my project to try and find the offending file. I was thinking that it must be something defined in a header file that was causing issues (like a static). It took a long time but I think I've found it. I had a header file that defines a number of structs. These are serialized to the wire so I had them 1 byte aligned using #pragma pack (push) which I put at the top of the file and #pragma pack (pop) at the bottom. But I then added a couple of #include statements after the first #pragma definition meaning that these includes were aligned incorrectly and caused some nondeterministic behavior. Thanks everyone that had a look. Should probably use the attribute syntax and I wouldn't had the problem. Offending code is below for completeness.
#pragma pack (push)
#pragma pack (1)
#include <string> //Wrong place for includes!
#include <Units.h>
typedef struct
{
....
}
#pragma pack (pop)
Thanks to everyone who had a look at initial problem.

Seeking STL-aware c++filt

In my development environment, I'm compiling a code base using GNU C++ 3.4.6. Code is under development, and unfortunately crashes now and then. It's nice to be able to run the traceback through a demangler, and I use c++filt 3.4. The problem comes when functions have a number of STL parameters. Consider
My_callback::operator()(
Status&,
std::set<std::string> const&,
std::vector<My_parameter*> const&,
My_attribute_set const&,
std::vector<My_parameter_base*> const&,
std::vector<My_parameter> const&,
std::set<std::string> const&
)
{
// ...
}
When this function is in the traceback, the mangled output on my platform is:
(_ZN30My_callbackclER11StatusRKSt3setISsSt4lessISsESaISsEERKSt6vectorIP13My_parameterSaISB_EERK17My_attribute_setRKS9_IP18My_parameter_baseSaISK_EERKS9_ISA_SaISA_EES8_+0x76a) [0x13ffdaa]
c++filt kindly demangles it to
(My_callback::operator()(Status&, std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<My_parameter*, std::allocator<My_parameter*> > const&, My_attribute_set const&, std::vector<My_parameter_base*, std::allocator<My_parameter_base*> > const&, std::vector<My_parameter, std::allocator<My_parameter> > const&, std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&)+0x76a) [0x13ffdaa]
This is the same problem as compiler errors encountered when using templates. However, the STL is a fairly regular and recognizable package of templates. So what I'm hoping is that someone out there has created an enhanced version of c++filt which would dump something closer to the original function signature. Any hints?
STLFilt simplifies and/or reformats long-winded C++ error and warning messages, with a focus on STL-related diagnostics. The result renders many of even the most cryptic diagnostics comprehensible.

Is C++ showing its age as programmers try to use it in ways it was never designed to be used?

Background
Using boost and other similar libraries is the easiest way to find compiler shortcomings, but is there a stage at which things go too far?
This mangled symbol:
_ZTSN5boost6spirit2qi6detail13parser_binderINS1_11alternativeINS_6fusion4consINS1_8sequenceINS6_INS1_16lexeme_directiveINS7_INS6_INS1_6actionINS1_9referenceIKNS1_4ruleIN9__gnu_cxx17__normal_iteratorIPKcSsEEFN7xxxxxxx2ir8carry_op2OpEvENS5_11unused_typeESM_EEEENS_7phoenix5actorINSQ_9compositeINSQ_11assign_evalENS5_6vectorINS0_14local_variableILi0EEENS0_8argumentILi0EEENS5_5void_ESZ_SZ_SZ_SZ_SZ_SZ_SZ_EEEEEEEENS6_INS1_10char_classINS0_3tag9char_codeINS15_5spaceENS0_13char_encoding5asciiEEEEENS5_3nilEEEEEEEEENS6_INS9_INS7_INS6_INSA_IKNSB_ISG_FSbIwSt11char_traitsIwESaIwEEvENSH_6parser11white_spaceISG_EESM_EEEENS6_INS1_12literal_charINS18_8standardELb1ELb0EEENS6_IS1R_NS6_IS1U_NS6_IS1R_NS6_IS1U_NS6_IS1R_NS6_IS1U_NS6_IS1R_S1C_EEEEEEEEEEEEEEEEEEEENSR_INSS_IST_NSU_INS0_9attributeILi0EEENSS_INSQ_6detail14construct_evalISJ_EENSU_ISW_SY_NSX_ILi1EEENSX_ILi2EEENSX_ILi3EEENSX_ILi4EEESZ_SZ_SZ_SZ_EEEESZ_SZ_SZ_SZ_SZ_SZ_SZ_SZ_EEEEEEEES1C_EEEEEENS6_INS7_INS6_IS1G_NS6_INS9_INS7_INS6_IS1R_NS6_IS1U_NS6_IS1R_NS6_IS1U_NS6_IS1R_NS6_IS1U_NS6_IS1U_S1W_EEEEEEEEEEEEEEEENSR_INSS_IST_NSU_IS26_NSS_IS29_NSU_ISW_SY_S2A_S2B_NSQ_5valueINS_8optionalIS1K_EEEES2C_SZ_SZ_SZ_SZ_EEEESZ_SZ_SZ_SZ_SZ_SZ_SZ_SZ_EEEEEEEES1C_EEEEEENS6_INS7_INS6_IS1G_NS6_INS9_INS7_IS21_EENSR_INSS_IST_NSU_IS26_NSS_IS29_NSU_ISW_SY_S2A_S2B_S2C_S2Y_SZ_SZ_SZ_SZ_EEEESZ_SZ_SZ_SZ_SZ_SZ_SZ_SZ_EEEEEEEES1C_EEEEEES1C_EEEEEEEEN4mpl_5bool_ILb0EEEEE
(1,388 characters)
Translates into (thanks c++filt!):
boost::spirit::qi::detail::parser_binder<boost::spirit::qi::alternative<boost::fusion::cons<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::lexeme_directive<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, xxxxxxx::ir::carry_op::Op ()(), boost::fusion::unused_type, boost::fusion::unused_type> const>, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::local_variable<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > >, boost::fusion::cons<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, boost::fusion::nil> > > >, boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::nil> > > > > > > > > >, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::phoenix::composite<boost::phoenix::detail::construct_eval<xxxxxxx::ir::carry_op>, boost::fusion::vector<boost::spirit::local_variable<0>, boost::spirit::argument<0>, boost::spirit::argument<1>, boost::spirit::argument<2>, boost::spirit::argument<3>, boost::spirit::argument<4>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> >, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > >, boost::fusion::nil> > >, boost::fusion::cons<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::lexeme_directive<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, xxxxxxx::ir::carry_op::Op ()(), boost::fusion::unused_type, boost::fusion::unused_type> const>, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::local_variable<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > >, boost::fusion::cons<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, boost::fusion::nil> > > >, boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::nil> > > > > > > > > >, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::phoenix::composite<boost::phoenix::detail::construct_eval<xxxxxxx::ir::carry_op>, boost::fusion::vector<boost::spirit::local_variable<0>, boost::spirit::argument<0>, boost::spirit::argument<1>, boost::spirit::argument<2>, boost::phoenix::value<boost::optional<std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > >, boost::spirit::argument<3>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> >, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > >, boost::fusion::nil> > >, boost::fusion::cons<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::lexeme_directive<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, xxxxxxx::ir::carry_op::Op ()(), boost::fusion::unused_type, boost::fusion::unused_type> const>, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::local_variable<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > >, boost::fusion::cons<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, boost::fusion::nil> > > >, boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard, true, false>, boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > ()(), xxxxxxx::parser::white_space<__gnu_cxx::__normal_iterator<char const*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::fusion::unused_type> const>, boost::fusion::nil> > > > > > > >, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::phoenix::composite<boost::phoenix::detail::construct_eval<xxxxxxx::ir::carry_op>, boost::fusion::vector<boost::spirit::local_variable<0>, boost::spirit::argument<0>, boost::spirit::argument<1>, boost::spirit::argument<2>, boost::spirit::argument<3>, boost::phoenix::value<boost::optional<std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > >, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> >, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > >, boost::fusion::nil> > >, boost::fusion::nil> > > >, mpl_::bool_<false> >
(12,980 characters)
Question
So my question is, in light of these (which technically don't pass the standard as I understand it, as the mangled name is over 1,024 characters), is this a sign that C++ is showing its age by not keeping up-to-date with the newest concepts? Or is it simply a sign that programmers are expecting too much from the language, and template libraries like boost are doing the best they can to facilitate this?
Wouldn't the language be showing its age if people did not apply it to new, bigger problems than originally intended?
I think this is exactly the opposite -- it shows that C++ is able to do something far more complex than was intended in the earliest versions of the language. How is that "showing its age"? C++ would be showing its age if it was still being used in the half-baked Java-like OOP style popular 15 years ago.
I think the fact that the usage of the language continues to evolve so much, even when the actual standard is unchanged, is a sign of strength.
Of course, C++ is showing its age in countless other ways, but I don't think this is one of them. On the contrary, this is one of C++'s redeeming features.
I think the fact that people keep coming up with new ways of using the features of C++ is the sign of its flexibility and power, not its aging.
In C++, the mangled symbol name is the equivalent of the call stack in other languages.
Show a 1970s asm programmer a call stack 23 deep, and they would say 'surely that is taking that new-fangled function stuff a bit far? How could you possibly debug and analyse something that complicated? You would need a special tool just to work out where everything was in memory'.
In fact, in the absence of a debugger, it would actually be really impractical to work with functions nested that deep - fine when things work, but when they break, it becomes impossibly complex to relate the memory image back to your code.
Eclipse CDT actually has a really nice feature where it can take pre-processor macros and expand them one step at a time, with backwards and forwards buttons. Display of templates, especially in error messages, needs something like that. So you can navigate between
list<T>
list<ptr<t>>
list<ptr<string<T>>>>
and so on.
As much as a wrench shows its age when it's being used to hammer in a nail.
I think it's a sign of aging for the standard limiting mangled name to 1,024 characters ;-)
Most of the resulting name is "noise", in that it's either specified by a typedef, or the arguments were defaults.
This IMHO, is more of an issue with where compiler implementors have decided to focus their efforts. Most compiler vendors spend more of their time ensuring that they implement the (complex) C++ standard correctly in a way that produces code that is as fast as they can possibly make it.
Providing pretty names for most is an after thought to the above.
Just to highlight this, with the exception of the last two items, the following mappings could be implemented in the demangler so that they always took place:
We can search and replace basic_string<const char *, ...> and replace it with string
We can search and replace basic_string<const wchar_t *, ...> and replace it with wstring
We can search and replace __gnu_cxx::__normal_iterator<char const *... and replace it with string::const_iterator
For display purposes we can remove boost::<namespace>:: and std:: as in context these don't add much.
Finally, C++ 0x will introduce template parameter packs and so we can remove things like boost:fusion::void_, boost:fusion::void_, boost:fusion::void_, since there will no longer be any need for preprocessor hacks to handle them.
After all of the above we have:
qi::detail::parser_binder<qi::alternative<cons<qi::sequence<cons<qi::lexeme_directive<qi::sequence<cons<qi::action<qi::reference<qi::rule<string::const_iterator, xxxxxxx::ir::carry_op::Op ()(), unused_type, unused_type> const>, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, vector<local_variable<0>, argument<0> > > > >, cons<qi::char_class<tag::char_code<tag::space, char_encoding::ascii> > > > > >, cons<qi::action<qi::sequence<cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const> > > > > > > > > > >, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, vector<attribute<0>, boost::phoenix::composite<boost::phoenix::detail::construct_eval<xxxxxxx::ir::carry_op>, vector<local_variable<0>, argument<0>, argument<1>, argument<2>, argument<3>, argument<4> > > > > > > > > >, cons<qi::sequence<cons<qi::lexeme_directive<qi::sequence<cons<qi::action<qi::reference<qi::rule<string::const_iterator, xxxxxxx::ir::carry_op::Op ()(), unused_type, unused_type> const>, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, vector<local_variable<0>, argument<0> > > > >, cons<qi::char_class<tag::char_code<tag::space, char_encoding::ascii> > > > > >, cons<qi::action<qi::sequence<cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const> > > > > > > > > > >, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, vector<attribute<0>, boost::phoenix::composite<boost::phoenix::detail::construct_eval<xxxxxxx::ir::carry_op>, vector<local_variable<0>, argument<0>, argument<1>, argument<2>, boost::phoenix::value<boost::optional<wstring> >, argument<3> > > > > > > > > >, cons<qi::sequence<cons<qi::lexeme_directive<qi::sequence<cons<qi::action<qi::reference<qi::rule<string::const_iterator, xxxxxxx::ir::carry_op::Op ()(), unused_type, unused_type> const>, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, vector<local_variable<0>, argument<0> > > > >, cons<qi::char_class<tag::char_code<tag::space, char_encoding::ascii> > > > > >, cons<qi::action<qi::sequence<cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const>, cons<qi::literal_char<char_encoding::standard, true, false>, cons<qi::reference<qi::rule<string::const_iterator, wstring()(), xxxxxxx::parser::white_space<string::const_iterator >, unused_type> const> > > > > > > > >, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, vector<attribute<0>, boost::phoenix::composite<boost::phoenix::detail::construct_eval<xxxxxxx::ir::carry_op>, vector<local_variable<0>, argument<0>, argument<1>, argument<2>, argument<3>, boost::phoenix::value<boost::optional<wstring> > > > > > > > > > > > > > >, mpl_::bool_<false> >
This string now has 5k characters (still a lot I agree) but its now less than half of the lenght of the original. Looking deeper at a sub section other patterns are visible:
cons<qi::reference<qi::rule<string::const_iterator, wstring()()
, xxxxxxx::parser::white_space<string::const_iterator >
, unused_type> const>
, cons<qi::literal_char<char_encoding::standard, true, false>,
cons<qi::reference<qi::rule<string::const_iterator, wstring()()
, xxxxxxx::parser::white_space<string::const_iterator >
, unused_type> const>
, cons<qi::literal_char<char_encoding::standard, true, false>,
cons<qi::reference<qi::rule<string::const_iterator, wstring()()
, xxxxxxx::parser::white_space<string::const_iterator >
, unused_type> const>
, cons<qi::literal_char<char_encoding::standard, true, false>,
cons<qi::reference<qi::rule<string::const_iterator, wstring()()
, xxxxxxx::parser::white_space<string::const_iterator >
, unused_type> const>
, cons<qi::literal_char<char_encoding::standard, true, false>,
cons<qi::reference<qi::rule<string::const_iterator, wstring()()
, xxxxxxx::parser::white_space<string::const_iterator >
, unused_type> const>
Again, 'boost' is using preprocessor tricks to allow a type to be built as if it is a vector. Similar to the case of void_ and nil, C++-0x parameter packs will avoid the need for the preprocessor to expand everything out 10 deep and the templates will be built to hold exactly what the user required. The above string takes up nearly a 1000 characters, and it is just one of 3 or 4 such strings in the original.
So if the question is: Is C++'03 showing its age, then the answer is "maybe" but only because it doesn't yet have template parameter packs.
I don't think the example you have given is a good example of the language being used "in ways it was never designed to be used". C++'s design explicitly allows for uses such as this. I think what it really shows is some of the language's weaknesses. The way templates and static typing work together in the language result in monstrosities such as your nice example. Even simpler uses of templates can easily result in absurdly long and complicated names. Programmers more familiar with other languages and less familiar with C++ would see things like this and immediately recognize that there's something not quite "right" with C++.
Now it's not to say that C++ is a crap language just because it has some shortcomings. All languages that I've ever used have some disadvantages. In the end, I think your example illustrates one of C++'s weaknesses better than it illustrates C++'s age.
C was designed in the spirit of "mechanism, not policy"; this means it can cope in strange places, like embedded devices or kernels, where a decent standard library may not be present. It opens up the door to writing portable code (though doesn't by any means guarantee it). Importantly, the language is powerful enough that important things are not treated as special cases (e.g. "printf" is just another function; you don't need special operators to do string formatting or any such). This means that the language can (to some extent) be built by its users, not its creators.
C++ has inherited some of this spirit. And in this spirit, people have made libraries that offer both functionality and fairly succinct syntax.
C++ is still used in cheap embedded systems because it cuts the costs of software development compared to C, but remains reasonably fast (and has actually been ported to the systems). For such reasons I don't think C++ is broken or showing its age.
But trying to turn it into a language suitable for "modern" pursuits is a simple case of using the wrong tool for the job. C++ has a place, but it is taking up a much wider area than it should be due to its historical popularity and momentum.
Please remember that the original C++ (C with classes) was a hack on C. Its goal was good - enable better organization of C programs by bringing in ideas from the OOP world. The objective was good but the way it was done - bad. C++, over the time, has grown in by piling feature upon feature on its tiny C base, which was designed for simplicity. What will happen if you construct a 100 storey building on a foundation meant for a 2 storey house? That is the disaster happened to C++. It culminated with the confusion around "concepts", which is now out of the new standard. Many of these new features were added to workaround the core C++'s inherent weaknesses. I think a statically typed language cannot be a good vehicle for OOP, at least if you go by Alan Kay's definition of that paradigm. When I use C++ I try to stick to the basic features like namespaces, standard template libraries and exceptions. I will create a C++ class only if an hierarchy of types makes absolute sense in the current context. I just ignore the rest of the 'advanced', complicated stuff.