C ++ Строковые независимые алгоритмы - PullRequest
0 голосов
/ 29 ноября 2010

Я пытаюсь получить технику для написания строковых алгоритмов, которая действительно не зависит от базового типа строки.

Справочная информация: прототипы для GetIndexOf и FindOneOf либо перегружены, либо являются шаблонными вариантами:

int GetIndexOf(const char * pszInner, const char * pszString);
const char * FindOneOf(const char * pszString, const char * pszSetOfChars);

Эта проблема возникает в следующей функции шаблона:

// return index of, or -1, the first occurrence of any given char in target
template <typename T>
inline int FindIndexOfOneOf(const T * str, const T * pszSearchChars)
{
    return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}

Цели:
1. Я хотел бы, чтобы этот код работал для CStringT <>, const char *,const wchar_t * (и должно быть тривиально для расширения до std :: string)
2. Я не хочу передавать что-либо в копии (только через const & или const *)

В попыткеРешив эти две задачи, я подумал, что смогу использовать сортировщик типов для получения правильных интерфейсов на лету:

namespace details {

    template <typename T>
    struct char_type_of
    {
        // typedef T type; error for invalid types (i.e. anything for which there is not a specialization)
    };

    template <>
    struct char_type_of<const char *>
    {
        typedef char type;
    };

    template <>
    struct char_type_of<const wchar_t *>
    {
        typedef wchar_t type;
    };

    template <>
    struct char_type_of<CStringA>
    {
        typedef CStringA::XCHAR type;
    };

    template <>
    struct char_type_of<CStringW>
    {
        typedef CStringW::XCHAR type;
    };

}

#define CHARTYPEOF(T) typename details::char_type_of<T>::type

, что позволяет:

template <typename T>
inline int FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars)
{
    return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}

Этодолжен гарантировать, что второй аргумент передается как const *, и не должен определять T (скорее, только первый аргумент должен определять T).

Но проблема с этим подходом состоит в том, что T, когда str является CStringT <>, является копией CStringT <>, а не ссылкой на нее: следовательно, у нас есть ненужная копия.

Попытка переписать вышеприведенное как:

template <typename T>
inline int FindIndexOfOneOf(T & str, const CHARTYPEOF(T) * pszSearchChars)
{
    return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}

Делает этоневозможно для компилятора (VS2008) создать правильный экземпляр FindIndexOfOneOf <> для:

FindIndexOfOneOf(_T("abc"), _T("def"));
    error C2893: Failed to specialize function template 'int FindIndexOfOneOf(T &,const details::char_type_of<T>::type *)'
    With the following template arguments: 'const char [4]'

Это общая проблема, с которой я столкнулся с шаблонами с момента их появления (да, я такой старый): Что по существу невозможно создать способ обработки как старых массивов в стиле C, так и более новых сущностей на основе классов (возможно, лучше всего это подчеркнуть с помощью const char [4] против CString <> &).

STLБиблиотека / std «решила» эту проблему (если ее можно назвать действительно решающей), вместо этого использовав повсюду пары итераторов вместо ссылки на саму вещь.Я мог бы пойти по этому пути, за исключением того, что это отстой IMO, и я не хочу, чтобы мой код был засорен двумя аргументами везде, где должен был бы быть должным образом обработанный единственный аргумент.подход - такой как использование некоторого вида stringy_traits - который позволил бы мне написать GetIndexOfOneOf <> (и другие подобные функции шаблона), где аргумент - это строка (не пара аргументов (Being, End]), а шаблон, которыйзатем генерируется правильно, основываясь на этом типе строкового аргумента ( const * или const CString <> & ).

Итак, вопрос: Как я могу написать FindIndexOfOneOf <> так, чтобы его аргументы могли быть любыми из следующих без создания копии базовых аргументов:
1. FindIndexOfOneOf (_T ("abc"), _T ("def"));
2. CString str; FindIndexOfOneOf (str, _T ("def"));
3. CString str; FindIndexOfOneOf (T ("abc"), str);
3. CString str;FindIndexOfOneOf (str, str);

объявления к этому, которые привели меня к этой точке:

Лучший способ объявить соответствующий тип строки CString <>
Шаблонные строковые литералы

Ответы [ 4 ]

2 голосов
/ 29 ноября 2010

Попробуйте это.

#include <type_traits>
inline int FindIndexOfOneOf(T& str, const typename char_type_of<typename std::decay<T>::type>::type* pszSearchChars)

Проблема в том, что когда вы делаете первый аргумент, ссылочный тип T становится следующим:

const char []

но вы хотите

const char*

Вы можете использовать следующее для этого преобразования.

std::decay<T>::type 

Документация говорит.

If is_array<U>::value is true, the modified-type type is remove_extent<U>::type *.
1 голос
/ 30 ноября 2010

Альтернатива моему предыдущему ответу, если вы не хотите устанавливать tr1.

Добавьте следующие специализации шаблона, чтобы охватить выводимый тип T, когда первый аргумент является ссылкой.

template<unsigned int N>
struct char_type_of<const wchar_t[N]>
{ 
    typedef wchar_t type;
};

template<unsigned int N>
struct char_type_of<const char[N]>
{ 
    typedef char type;
};
1 голос
/ 29 ноября 2010

Судя по вашим комментариям об итераторах, вы, возможно, еще не полностью рассмотрели возможные варианты. Я ничего не могу поделать с личными предпочтениями, но опять же ... ИМХО, это не должно быть труднопреодолимым препятствием для принятия разумного решения, которое должно быть взвешено и сбалансировано технически .

template < typename Iter >
void my_iter_fun(Iter start, Iter end)
{
 ...
}
template < typename T >
void my_string_interface(T str)
{
  my_iter_fun(str.begin(), str.end());
}
template < typename T >
void my_string_interface(T* chars)
{
  my_iter_fun(chars, chars + strlen(chars));
}
1 голос
/ 29 ноября 2010

Вы можете использовать Boost's enable_if и type_traits для этого:

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

// Just for convenience
using boost::enable_if;
using boost::disable_if;
using boost::is_same;

// Version for C strings takes param #1 by value
template <typename T>
inline typename enable_if<is_same<T, const char*>, int>::type
FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars)
{
    return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}

// Version for other types takes param #1 by ref
template <typename T>
inline typename disable_if<is_same<T, const char*>, int>::type
FindIndexOfOneOf(T& str, const CHARTYPEOF(T) * pszSearchChars)
{
    return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}

Вероятно, вам следует расширить первый регистр для обработки строк char и wchar_t, что можно сделать с помощью or_ из библиотеки Boost MPL .

Я бы также порекомендовал сделать так, чтобы версия, использующая ссылку, взяла вместо этого ссылку на const. Это просто позволяет избежать создания экземпляров 2-х отдельных версий кода (в нынешнем виде T будет выведен как const-тип для const-объектов и неконстантный тип для не-const-объектов; изменение типа параметра на T const& str означает, что T всегда будет выводиться как неконстантный тип.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...