SFINAE: Вызывать функцию только если набор аргументов соотве
От: Molchalnik  
Дата: 24.05.20 17:50
Оценка:
  Тыкните, чтобы прочитать предысторию/постановку задачи
Надо было сделать обёртку для std::thread, которая запускала в параллельном режиме раннэйбл/кэллбек/функцию_потока . Но в одном случае с одним набором аргументов, а в другом случае с другим, потому что в одном варианте поток должен быть владельцем части передаваемых данных. Естественно, ошибка компиляции — у прототипа функции_потока в одном случае набор параметров под первый вариант, а в другом под второй. Надо было сделать SFINAE, которая для нессотвествующих параметров генерировала сообщение об ошибке, а для соотвествующих — вызывала бы функцию, и всё это на этапе компиляции.


SFINAE функция, которая на этапе компиляции проверяет, соответствует ли набор аргументов функтору/кэллбеку/функции, и если соответствует, генерирует вызов функции, а если не соответствует, генерирует пользовательское сообщение об ошибке. Работает как для функторов, лямбд, так и для указателей на функцию. Не проверял для случая наличия перегруженных функций (с одним названием и разными аргументами), но по идее , должно работать и в этом случае.

Сделано самым упрощённым способом — через if constexpr, что добавляет читаемости.

ссылка на код на колиру

#include <iostream>
#include <string>
#include <vector>

template <typename CallbackTn, typename = void> struct  IsFunctor : public std::false_type {    
    }; 
template <typename CallbackTn > struct IsFunctor < CallbackTn, std::void_t< decltype(&CallbackTn::operator() ) > > : public std::true_type {
};

template <typename CallbackTn, typename ErrorReactionTn, typename... Args> void CallIfArgsCorrect(   
    CallbackTn && callback, 
    ErrorReactionTn error_fun  , 
    Args... args   ) {
  if constexpr ( IsFunctor< CallbackTn >::value ) {
    typedef void (CallbackTn::*MemberType)(Args...) const;
    if constexpr ( std::is_same< decltype(&CallbackTn::operator()), MemberType >::value ) {
      callback( std::forward<Args>(args)... );          
    } else {
      error_fun();
    }
  } else {
    if constexpr ( std::is_same<CallbackTn, void (*)(Args...)>::value ) {
      callback( std::forward<Args>(args)... );          
    } else {
      error_fun();
    }
  }
}


template <typename CallbackTn, typename... Args> void Call( CallbackTn && callback, Args&& ... args ) { 
  CallIfArgsCorrect( 
      std::forward<CallbackTn>(callback), 
      []() {
        std::cout<<"Error args"<< std::endl;
      }, 
      std::forward<Args>(args)... );
}

void Fun( int x ) {
    std::cout << "All correct" << std::endl;
    }

int main()
{
    int x= 3 , y [[maybe_unused]]  = 4;
    Call( [y]( int var ) -> void { std::cout << "All correct, result = " << var * y << std::endl; }, x );
    Call( [y]( int var ) -> void { std::cout << "All correct, result = " << var * y << std::endl; }, x, y );
    Call( &Fun, x );
    Call( &Fun, x, y );
    return 0;
}


Приветствую замечания, которые ведут к допиливанию кода функции
Отредактировано 24.05.2020 17:53 Molchalnik . Предыдущая версия .
sfinae вызов функции function call c++
Re: SFINAE: Вызывать функцию только если набор аргументов соотве
От: vopl Россия  
Дата: 24.05.20 18:26
Оценка:
Здравствуйте, Molchalnik, Вы писали:


M>Приветствую замечания, которые ведут к допиливанию кода функции


Еще проще, без собственного SFINAE, на базе стандартной библиотеки

#include <iostream>
#include <functional>

template <typename CallbackTn, typename... Args>
decltype(auto) Call( CallbackTn && callback, Args&& ... args ) {
    if constexpr ( std::is_invocable_v<CallbackTn&&, Args&&...> )
        return std::invoke(std::forward<CallbackTn>(callback), std::forward<Args>(args)...);
    std::cout<<"Error args"<< std::endl;
}

void Fun( int /*x*/ ) {
    std::cout << "All correct" << std::endl;
}

int main() {
    int x= 3 , y [[maybe_unused]]  = 4;
    Call( [y]( int var ) -> void { std::cout << "All correct, result = " << var * y << std::endl; }, x );
    Call( [y]( int var ) -> void { std::cout << "All correct, result = " << var * y << std::endl; }, x, y );
    Call( &Fun, x );
    Call( &Fun, x, y );
    return 0;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.