Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems specializing variable template function

I am writing a function inListi() which takes at least one argument and compares the first argument to thes list of all subsequent arguments. returns true if first argument == an element in the list, otherwise false. So:

if( inListi( 1.2, 2.3, 4.5, 1.2))
  std::cout << "Returns true because last argument equals the first argument." << endl;

if( inListi( "hello", "world", "HEllo"))
    std::cout << "This should print out because of the last argument." << endl;

Problem is, it doesn't work. I have the code below. For char[N], I copy N portions of the array into a string before continuing. I want to do this, because I might be passed a char[N] that is not null terminated.

Anyways, the code is below. Most of the code is redundant and deal with const and combinations of one argument being const[N] and the other not of that type. (By the way, is there a way to reduce this repetition?)

#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <typeinfo>
#include <type_traits>

#include <boost/algorithm/string.hpp>

using namespace std;

////////////////////////////////////////////////////////////////////////////////
// inListi
////////////////////////////////////////////////////////////////////////////////
template<typename T>
bool inListi(T&& value)
{
    return false;
}


template<typename FirstType, typename SecondType, typename ... Rest>
bool inListi(FirstType&& first, SecondType&& second, Rest ... rest)
{
    cout << "GENERIC inListi" << endl;
    cout << "first is " << typeid(first).name() << endl;
    cout << "second is " << typeid(second).name() << endl;

    if( first == second)
        return true;
    else return inListi( first, rest...);
}


// We specialize the inListi for strings.  We lower the case.
// but what if char[n] is passed?  We have specializations that
// convert that to strings.
template<typename ... Rest>
bool inListi( string &&first, string &&second,  Rest ... rest) {
    string lFirst = first;
    string lSecond = second;

    cout << "LOWERED" << endl;

    boost::algorithm::to_lower( lFirst);
    boost::algorithm::to_lower( lSecond);

    if( lFirst == lSecond)
        return true;
    else return inListi( first, rest...);
}



// Specializations for when we are given char-arrays.  We copy the
// the arrays into a string upto the size of the array.  This is done
// to take care of the case of when the char-array is not nul-terminated.
// The amount repetition is to permutate over which argument is a char-array
// and also for const-ness.
template<int F, typename SecondType, typename ... Rest>
bool inListi( char (&&first)[F], SecondType &&second, Rest ... rest) {
    string strFirst = string( first, F);
    cout << "arr, type, rest" << endl;
    return inListi( strFirst, second, rest...);
}
template<int F, typename SecondType, typename ... Rest>
bool inListi( const char (&&first)[F], SecondType &&second, Rest ... rest) {
    string strFirst = string( first, F);
    cout << "const arr, type, rest" << endl;
    return inListi( strFirst, second, rest...);
}
template<typename FirstType, int S, typename ... Rest>
bool inListi( FirstType &&first, char (&&second)[S], Rest ... rest) {
    string strSecond = string( second, S);
    cout << "type, arr, rest" << endl;
    return inListi( first, strSecond, rest...);
}
template<typename FirstType, int S, typename ... Rest>
bool inListi( FirstType &&first, const char (&&second)[S], Rest ... rest) {
    string strSecond = string( second, S);
    cout << "type, const arr, rest" << endl;
    return inListi( first, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( char (&&first)[F], char (&&second)[S], Rest ... rest) {
    string strFirst  = string( first, F);
    string strSecond = string( second, S);
    cout << "arr, arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( const char (&&first)[F], char (&&second)[S], Rest ... rest) {
    string strFirst  = string( first, F);
    string strSecond = string( second, S);
    cout << "const arr, arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( char (&&first)[F], const char (&&second)[S], Rest ... rest) {
    string strFirst  = string( first, F);
    string strSecond = string( second, S);
    cout << "arr, const arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( const char (&&first)[F], const char (&&second)[S], Rest ... rest) {
    string strFirst = string( first, F);
    string strSecond = string( second, S);
    cout << "const arr, const arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}


int main() {

    if( inListi( "Hello", "World", "HEllo"))
        cout << "Hello is in the listi." << endl;
    else
        cout << "Hello is not in the listi." << endl;

    return 0;
}

The output of the program is the following:

[bitdiot foo]$ g++ forStackOverflow.cpp -std=gnu++0x
[bitdiot foo]$ ./a.out
GENERIC inListi
first is A6_c
second is A6_c
GENERIC inListi
first is A6_c
second is PKc
Hello is not in the listi.

Notice, that none of the intermediary code is called, it goes straight to using the generic version. Also, another thing that looks weird is the 'PKc'. Which I am assuming is type char*. Now, why would it have a different type?

Anyways, thanks!!

like image 341
Bitdiot Avatar asked Jan 22 '26 11:01

Bitdiot


1 Answers

It looks like you could use an overload for const char* (which is perhaps what the PKc refers to).

Maybe something like:

template<typename ... Rest>
bool inListi(const char *first, const char *second, Rest... rest) {
    cout << "const char*, const char*, rest" << endl;
    return inListi(string(first), string(second), rest...);
} 

Two other things:

  • note the use of temporaries so that we'll call the string&& version
  • a const char* overload will match the first call, but you might need more overloads to handle subsequent calls

Another edit: some great feedback on prevent array decay in parameter pack expansion shows us how this can be done by using rvalue references on the parameter pack:

#include <iostream>
#include <string>
#include <utility>
#include <algorithm>
#include <cctype>

template <typename T>
bool inListi(T&& value)
{
  return false;
}

template <typename FirstType, typename SecondType, typename... Rest>
bool inListi(FirstType&& first, SecondType&& second, Rest&&... rest)
{
  if (first == second)
    return true;
  else
    return inListi(std::forward<FirstType&&>(first), rest...);
}

template <int N, int M, typename... Rest>
bool inListi(char (&first)[N], char (&second)[M], Rest&&... rest)
{
  std::string lFirst(first, N);
  std::transform(lFirst.begin(), lFirst.end(), lFirst.begin(), ::tolower);
  std::string lSecond(second, M);
  std::transform(lSecond.begin(), lSecond.end(), lSecond.begin(), ::tolower);

  if (lFirst == lSecond)
    return true;
  else
    return inListi(first, rest...);
}

template <typename... Rest>
bool inListi(const char *first, const char *second, Rest&&... rest)
{
  std::string lFirst(first);
  std::transform(lFirst.begin(), lFirst.end(), lFirst.begin(), ::tolower);
  std::string lSecond(second);
  std::transform(lSecond.begin(), lSecond.end(), lSecond.begin(), ::tolower);

  if (lFirst == lSecond)
    return true;
  else
    return inListi(first, rest...);
}

int main() {
  char a[5] = {'H','e','l','l','o'};
  char b[5] = {'W','o','r','l','d'};
  char c[5] = {'H','E','l','l','o'};
  std::cout << inListi(a, b, c) << '\n';
  std::cout << inListi("Hello", "World", "HEllo") << '\n';
  std::cout << inListi(5, 42, 5) << '\n';
}

This works as long as all arguments have the same type. If you want to start mixing types, you'll probably need to start writing all of the appropriate pairs of overloads.

like image 173
Nate Kohl Avatar answered Jan 24 '26 06:01

Nate Kohl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!