I'm working on a C++ project in High Performance Computing using MPI. I have a function with a few different overloads, that I use to convert different types into strings:
void append(std::string& s, int value);
void append(std::string& s, void* value);
void append(std::string& s, MPI_Request request);
This worked fine while I was using Open MPI. In OpenMPI, MPI_Request
is an alias for ompi_request_t*
, so each overload has a different signature.
Recently, however, I tried compiling the code with MPICH. In MPICH, MPI_Request
is an alias for int
, and the result is that the above code fails to compile because append
is defined for int
twice:
/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)
How should I write append(std::string&, MPI_Request)
so that the compiler ignores it when MPI_Request
is defined as an int
, but recognizes it when MPI_Request
is a library type?
enable_if
failsI attempted to write a solution based on std::enable_if
, where the function is only enabled if MPI_Request
is a different type than 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);
}
This fails because when MPI_Request
is the same as int
, the statement is always false, and since it's not dependent on any template parameters, the compiler flat-out refuses to compile it.
How do I fix this, and make append
contingent on MPI_Request
being different from int
?
That's unfortunate. The bottom line is that enable_if
can only be used inside SFINAE context which requires the T - template. To iterate on your idea we can specify our requirements in the return type so that the template matches only MPI_Request
and only if the MPI_Request
type is not an 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);
Full example, you can even see which cout
line gets inlined into main.
You can make it a function template with defaulted template parameter, which allows SFINAE to work:
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);
}
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With