FairMQ/fairmq/tools/FairMQTools.h
2017-03-02 13:25:06 +01:00

223 lines
6.7 KiB
C++

#ifndef FAIRMQTOOLS_H_
#define FAIRMQTOOLS_H_
#ifndef _GNU_SOURCE
#define _GNU_SOURCE // To get defns of NI_MAXSERV and NI_MAXHOST
#endif
#include "FairMQLogger.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <boost/algorithm/string.hpp> // trim
#include <map>
#include <string>
#include <iostream>
#include <type_traits>
#include <array>
using namespace std;
namespace FairMQ
{
namespace tools
{
// make_unique implementation, until C++14 is default
template<typename T, typename ...Args>
unique_ptr<T> make_unique(Args&& ...args)
{
return unique_ptr<T>(new T(forward<Args>(args)...));
}
// returns a map with network interface names as keys and their IP addresses as values
int getHostIPs(map<string, string>& addressMap)
{
struct ifaddrs *ifaddr, *ifa;
int s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
return -1;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
{
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET)
{
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (s != 0)
{
cout << "getnameinfo() failed: " << gai_strerror(s) << endl;
return -1;
}
addressMap.insert(pair<string, string>(ifa->ifa_name, host));
}
}
freeifaddrs(ifaddr);
return 0;
}
// get IP address of a given interface name
string getInterfaceIP(string interface)
{
map<string, string> IPs;
FairMQ::tools::getHostIPs(IPs);
if (IPs.count(interface))
{
return IPs[interface];
}
else
{
LOG(ERROR) << "Could not find provided network interface: \"" << interface << "\"!, exiting.";
return "";
}
}
// get name of the default route interface
string getDefaultRouteNetworkInterface()
{
array<char, 128> buffer;
string interfaceName;
#ifdef __APPLE__ // MacOS
unique_ptr<FILE, decltype(pclose) *> file(popen("route -n get default | grep interface | cut -d \":\" -f 2", "r"), pclose);
#else // Linux
unique_ptr<FILE, decltype(pclose) *> file(popen("ip route | grep default | cut -d \" \" -f 5", "r"), pclose);
#endif
if (!file)
{
LOG(ERROR) << "Could not detect default route network interface name - popen() failed!";
return "";
}
while (!feof(file.get()))
{
if (fgets(buffer.data(), 128, file.get()) != NULL)
{
interfaceName += buffer.data();
}
}
boost::algorithm::trim(interfaceName);
if (interfaceName == "")
{
LOG(ERROR) << "Could not detect default route network interface name";
}
else
{
LOG(DEBUG) << "Detected network interface name for the default route: " << interfaceName;
}
return interfaceName;
}
#if defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#endif
// below are SFINAE template functions to check for function member signatures of class
namespace details
{
// test, at compile time, whether T has BindSendHeader member function with returned type R and argument ...Args type
template<class T, class Sig, class=void>
struct has_BindSendHeader : false_type {};
template<class T, class R, class... Args>
struct has_BindSendHeader
<T, R(Args...), typename enable_if<
is_convertible<decltype(declval<T>().BindSendHeader(declval<Args>()...)), R>::value || is_same<R, void>::value>::type
>:true_type {};
// test, at compile time, whether T has BindGetSocketNumber member function with returned type R and argument ...Args type
template<class T, class Sig, class=void>
struct has_BindGetSocketNumber : false_type {};
template<class T, class R, class... Args>
struct has_BindGetSocketNumber
<T, R(Args...), typename enable_if<
is_convertible<decltype(declval<T>().BindGetSocketNumber(declval<Args>()...)), R>::value || is_same<R, void>::value>::type
>:true_type {};
// test, at compile time, whether T has GetHeader member function with returned type R and argument ...Args type
template<class T, class Sig, class=void>
struct has_GetHeader : false_type {};
template<class T, class R, class... Args>
struct has_GetHeader
<T, R(Args...), typename enable_if<
is_convertible<decltype(declval<T>().GetHeader(declval<Args>()...)), R>::value || is_same<R, void>::value>::type
>:true_type {};
// test, at compile time, whether T has BindGetCurrentIndex member function with returned type R and argument ...Args type
template<class T, class Sig, class=void>
struct has_BindGetCurrentIndex : false_type {};
template<class T, class R, class... Args>
struct has_BindGetCurrentIndex
<T, R(Args...), typename enable_if<
is_convertible<decltype(declval<T>().BindGetCurrentIndex(declval<Args>()...)), R>::value || is_same<R, void>::value>::type
>:true_type {};
} // end namespace details
#if defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
// Alias template of the above structs
template<class T, class Sig>
using has_BindSendHeader = integral_constant<bool, details::has_BindSendHeader<T, Sig>::value>;
template<class T, class Sig>
using has_BindGetSocketNumber = integral_constant<bool, details::has_BindGetSocketNumber<T, Sig>::value>;
template<class T, class Sig>
using has_GetHeader = integral_constant<bool, details::has_GetHeader<T, Sig>::value>;
template<class T, class Sig>
using has_BindGetCurrentIndex = integral_constant<bool, details::has_BindGetCurrentIndex<T, Sig>::value>;
// enable_if Alias template
template<typename T>
using enable_if_has_BindSendHeader = typename enable_if<has_BindSendHeader<T, void(int)>::value, int>::type;
template<typename T>
using enable_if_hasNot_BindSendHeader = typename enable_if<!has_BindSendHeader<T, void(int)>::value, int>::type;
template<typename T>
using enable_if_has_BindGetSocketNumber = typename enable_if<has_BindGetSocketNumber<T, int()>::value, int>::type;
template<typename T>
using enable_if_hasNot_BindGetSocketNumber = typename enable_if<!has_BindGetSocketNumber<T, int()>::value, int>::type;
template<typename T>
using enable_if_has_BindGetCurrentIndex = typename enable_if<has_BindGetCurrentIndex<T, int()>::value, int>::type;
template<typename T>
using enable_if_hasNot_BindGetCurrentIndex = typename enable_if<!has_BindGetCurrentIndex<T, int()>::value, int>::type;
template<typename T>
using enable_if_has_GetHeader = typename enable_if<has_GetHeader<T, int()>::value, int>::type;
template<typename T>
using enable_if_hasNot_GetHeader = typename enable_if<!has_GetHeader<T, int()>::value, int>::type;
} // namespace tools
} // namespace FairMQ
#endif