Hi, I am trying to write function overloaded function template, but I get some kind of an instantiation problem and here is my code:

/* 
 * File:   main.cpp
 * Author: cppguy
 *
 * Created on 20 January 2011, 19:10
 */

#include <cstdlib>
#include <iostream>
#include <cctype>
#include <cwctype>
#include <typeinfo>

using namespace std;

/* */
template<typename t, typename funct_>
void trimr(std::basic_string<t>& s, funct_ f) {
    if (s.empty())
        return;

    typename std::basic_string<t>::iterator it;
    for (it = s.end(); it != s.begin() && *--it == f(*it););
    if (*it != f(*it))
        ++it;
    s.erase(it, s.end());
}

template<typename t>
void trimr(std::basic_string<t>& s) {
    if (typeid (std::basic_string<char>()) == typeid (s))
        trimr(s, isspace); // no matching function call
    else
        trimr(s, iswspace); //no matching function call
}

int main(int argc, char** argv) {

    string s("zoo");
    wstring ws(L"fee");
    trimr(s);
    trimr(ws);
    cout << s << endl;
    wcout << ws << endl;

    return 0;
}

And compiler gives this nice msg:

"/usr/bin/make" -f nbproject/Makefile-Debug.mk QMAKE= SUBPROJECTS= .build-conf
make[1]: Entering directory `/home/cppguy/NetBeansProjects/whitespace_triming'
"/usr/bin/make"  -f nbproject/Makefile-Debug.mk dist/Debug/GNU-Linux-x86/whitespace_triming
make[2]: Entering directory `/home/cppguy/NetBeansProjects/whitespace_triming'
mkdir -p build/Debug/GNU-Linux-x86
rm -f build/Debug/GNU-Linux-x86/main.o.d
g++    -c -g -MMD -MP -MF build/Debug/GNU-Linux-x86/main.o.d -o build/Debug/GNU-Linux-x86/main.o main.cpp
main.cpp: In function ‘void trimr(std::basic_string<_CharT, std::char_traits<_CharT>, std::allocator<_CharT> >&) [with t = char]’:
main.cpp:41:   instantiated from here
main.cpp:32: error: no matching function for call to ‘trimr(std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, <unresolved overloaded function type>)’
main.cpp: In function ‘void trimr(std::basic_string<_CharT, std::char_traits<_CharT>, std::allocator<_CharT> >&) [with t = wchar_t]’:
main.cpp:42:   instantiated from here
main.cpp:32: error: no matching function for call to ‘trimr(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >&, <unresolved overloaded function type>)’
make[2]: *** [build/Debug/GNU-Linux-x86/main.o] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2
make[2]: Leaving directory `/home/cppguy/NetBeansProjects/whitespace_triming'
make[1]: Leaving directory `/home/cppguy/NetBeansProjects/whitespace_triming'

BUILD FAILED (exit value 2, total time: 362ms)

Edited 5 Years Ago by cppgangster: n/a

You forgot to take the address of the functions. Your lines 32 and 34 should be like so:

trimr(s, &isspace); // notice the &
trimr(s, &iswspace); // notice the &

I managed to do this with some tricks..but still not satisfied, but should be..

#include <cstdlib>
#include <iostream>
#include <cctype>
#include <string>
#include <cwctype>
#include <typeinfo>

using namespace std;

bool iz_space(char ch){
    return isspace(ch);
}
bool iz_wspace(wchar_t ch){
    return iswspace(ch);
}


template<typename t, typename funct_>
void trimr(std::basic_string<t>& s, funct_ f) {
    if (s.empty())
        return;

    typename std::basic_string<t>::iterator it;
    for (it = s.end(); it != s.begin() && f(*--it););
    if (*it != f(*it))
        ++it;
    s.erase(it, s.end());
}

template<typename t>
void trimr(std::basic_string<t>& s){
    if (typeid(basic_string<char>())==typeid(s))
        trimr(s,iz_space);
    else
        trimr(s,iz_wspace);
}

int main(int argc, char** argv) {

    string s("zoo  ");
    wstring ws(L"fee   ");
    cout<<s<<".."<<endl;
    wcout << ws <<".."<< endl;
    trimr(s);
    trimr(ws);
    cout<<s<<".."<<endl;
    wcout << ws <<".."<< endl;

    return 0;
}

The program works as long I don't pass to overloaded function to the function that uses as it as template type, so it can not resolve the type, but if you pass template function and specifying in advance the type in <> can I do this? I need to investigate this shit..

Edited 5 Years Ago by cppgangster: n/a

Sure you can specify the type in advance using the <>. That is called "explicit instantiation" of the template. But, as I said before, you need to use a function pointer type, this means that you must take the address (using &) of the function before you pass it to the function template. For instance, you could do this:

template<typename t>
void trimr(std::basic_string<t>& s){
    if (typeid(basic_string<char>())==typeid(s))
        trimr<char,bool(*)(char)>(s,&iz_space);
    else
        trimr<wchar_t,bool(*)(wchar_t)>(s,&iz_wspace);
}

But I don't think that the explicit specification of the template arguments is what makes the above work. What makes it work is the & before the function, to make them function pointers.

BTW, have you considered using template specialization to improve your implementation. Here is what you could do:

template<typename t>
void trimr(std::basic_string<t>& s); //leave this intentionally not implemented.

//specialize it for char and wchar_t:
template <>
void trimr(std::basic_string<char>& s) {
  trimr(s,&iz_space);
}

template <>
void trimr(std::basic_string<wchar_t>& s) {
  trimr(s,&iz_wspace);
}

This way, if someone tries to call trimr with anything other than "char" or "wchar_t", it will throw a compile-time error like (for float instead of char) "unresolved reference to trimr with [t = float]".

But, to do it even better, I would suggest using type traits, as so:

#include <cstdlib>
#include <iostream>
#include <cctype>
#include <string>
#include <cwctype>
#include <typeinfo>

using namespace std;

template <typename T>
struct my_char_trait { }; //intentionally left blank.

//specialize for both char types:
template <>
struct my_char_trait<char> {
  static bool is_space(char ch) { return isspace(ch); };
};
template <>
struct my_char_trait<char> {
  static bool is_space(char ch) { return iswspace(ch); };
};

//now define you trimr function:
template<typename t>
void trimr(std::basic_string<t>& s) {
    if (s.empty())
        return;

    typename std::basic_string<t>::iterator it;
    for (it = s.end(); it != s.begin() && my_char_trait<t>::is_space(*--it););
    if (*it != my_char_trait<t>::is_space(*it))
        ++it;
    s.erase(it, s.end());
}

int main(int argc, char** argv) {

    string s("zoo  ");
    wstring ws(L"fee   ");
    cout<<s<<".."<<endl;
    wcout << ws <<".."<< endl;
    trimr(s);
    trimr(ws);
    cout<<s<<".."<<endl;
    wcout << ws <<".."<< endl;

    return 0;
}

In the above, if trimr is called with a type t other than char or wchar_t, it will throw a compile-time error like (for float instead of char) "function 'is_space' is not a member of my_char_trait<float>, in function trimr, instantiated from function main() at line XX", which would be pretty obvious to the programmer what the error is (float is not a char! and the problem is in main when calling trimr).

I hope you are starting to appreciate the kinds of awesome things that templates are capable of. There are, of course, many more ways in which you can implement the above, depending on what you want. If you are doing string parsing, it might be good to take a look at Boost.Spirit and Boost.Regex (either for inspiration or to use them instead of reinventing the wheel).

Edited 5 Years Ago by mike_2000_17: n/a

This would suffice for char and wchar_t.
Also for any user defined CHAR_TYPE that has defined a ctype facet.

#include <string>
#include <locale>

template< typename CHAR_TYPE, typename TRAITS_TYPE, typename ALLOCATOR_TYPE >
void trim_r( std::basic_string<CHAR_TYPE,TRAITS_TYPE,ALLOCATOR_TYPE>& str )
{
    std::locale loc( std::locale(), new std::ctype<CHAR_TYPE>() ) ;
    auto iter = str.rbegin() ; // C++1X
    while( (iter!=str.rend()) && std::isspace(*iter,loc) ) ++iter ;
    str.erase( iter.base(), str.end() ) ;
}

A slightly more efficient version of the above:

template< typename CHAR_TYPE, typename TRAITS_TYPE, typename ALLOCATOR_TYPE >
void trim_r( std::basic_string<CHAR_TYPE,TRAITS_TYPE,ALLOCATOR_TYPE>& str )
{
    struct _is_space : public std::ctype<CHAR_TYPE> 
    { bool operator() ( CHAR_TYPE c ) const { return is( space, c ) ; } } ;
    _is_space is_space ;

    auto iter = str.rbegin() ; 
    while( (iter!=str.rend()) && is_space(*iter) ) ++iter ;
    str.erase( iter.base(), str.end() ) ;
}
This article has been dead for over six months. Start a new discussion instead.