Условно включить не шаблонную функцию c ++ - PullRequest
5 голосов
/ 22 мая 2019

Фон

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

void append(std::string& s, int value);
void append(std::string& s, void* value);
void append(std::string& s, MPI_Request request); 

Это работало нормально, когда я использовал Open MPI. В OpenMPI MPI_Request является псевдонимом для ompi_request_t*, поэтому каждая перегрузка имеет свою сигнатуру.

Однако недавно я попытался скомпилировать код с помощью MPICH. В MPICH MPI_Request является псевдонимом для int, и в результате вышеприведенный код не может быть скомпилирован, потому что append определен для int дважды:

/home/me/NimbleSM/src/mpi-buckets/src/mpi_err.hpp: At global scope:
/home/me/NimbleSM/src/mpi-buckets/src/mpi_err.hpp:28:6: error: redefinition of ‘void append(std::__cxx11::string&, int)’
 void append(std::string& s, int i) { s.append(std::to_string(i)); }
      ^~~
/home/me/NimbleSM/src/mpi-buckets/src/mpi_err.hpp:17:6: note: ‘void append(std::__cxx11::string&, MPI_Request)’ previously defined here
 void append(std::string& s, MPI_Request request)

Вопрос

Как мне написать append(std::string&, MPI_Request), чтобы компилятор игнорировал его, когда MPI_Request определен как int, но распознал, когда MPI_Request - это тип библиотеки?

Попытка решения: enable_if не удалось

Я попытался написать решение на основе std::enable_if, где функция включается только в том случае, если MPI_Request отличается от int.

auto append(std::string& s, MPI_Request request)
    -> typename std::enable_if<!std::is_same<MPI_Request, int>::value, void>::type
{ 
    str(s, (void*)request); 
}

Это терпит неудачу, потому что, когда MPI_Request совпадает с int, оператор всегда ложен, и, поскольку он не зависит от каких-либо параметров шаблона, компилятор отказывается компилировать его.

Как это исправить и сделать append зависимым от MPI_Request, отличным от int?

Ответы [ 3 ]

2 голосов
/ 22 мая 2019

Вы можете сделать его шаблоном функции с параметром шаблона по умолчанию, который позволяет SFINAE работать:

void append(std::string& s, int value);
void append(std::string& s, void* value);

template <typename MPI_Request_ = MPI_Request,
    typename std::enable_if<
        !std::is_same<MPI_Request_, int>::value
        && std::is_same<MPI_Request_, MPI_Request>::value
    , int>::type = 0>
void append(std::string& s, MPI_Request_ request)
{ 
    str(s, (void*)request);
    // Or, if you want the implementation in the source file,
    // call another function like: append_mpi_request(s, request);
}

Демо

2 голосов
/ 22 мая 2019

Это прискорбно. Суть в том, что enable_if может использоваться только внутри контекста SFINAE, для которого требуется T-шаблон. Чтобы повторить вашу идею, мы можем указать наши требования в типе возврата, чтобы шаблон соответствовал только MPI_Request и только если тип MPI_Request не является int.

#include <string>
#include <type_traits>
using MPI_Request = int;// Or something else

template<typename T>
using T_is_MPI_and_not_also_int = std::conjunction<std::is_same<T,MPI_Request>, std::negation<std::is_same<MPI_Request,int>>>;

template<typename T>
std::enable_if_t<T_is_MPI_and_not_also_int<T>::value,void> 
append(std::string& s, T request);

Полный пример , вы даже можете увидеть, какая строка cout вставляется в основную.

0 голосов
/ 22 мая 2019

Вот низкотехнологичное решение.Используйте макрос препроцессора.

void append(std::string& s, int value);
void append(std::string& s, void* value);

#ifdef MPI_REQUEST_IS_INT
// No need to declare append() for MPI_Request
#else
void append(std::string& s, MPI_Request request); 
#endif

Используйте ту же стратегию в файле .cpp.

...