Вывод типа шаблонного параметра функции
От: Alex34 Израиль  
Дата: 18.03.20 07:57
Оценка:
Доброе времжя суток и здоровья в эти непростые дни

Пишу библиотеку , которая реализует функции математического редактора Wolfram Mathematica.

Например в редакторе есть функция Union со следующеий спецификацией


Union [list1,list2,...]


gives a sorted list of all the distinct elements that appear in any of the list i.

или


Union [list]


gives a sorted version of a list, in which all duplicated elements have been dropped.

Здесь привожу тест для ясности

Union[{a, b, a, c}, {d, a, e, b}, {c, a}]
{a, b, c, d, e}



Для реализации данноий функции определил следужющее :

template<typename T, typename... Ts >
    T Union(const T& c1, const Ts&... rest)
{
..
}

template <typename T >
    T Union(const T& c1, const T& c2 )
{
..
}


Пример вызова :

vector<char> list_1 = { 'a', 'b', 'a', 'c' };
vector<char> list_2 = { 'd', 'a', 'e', 'b' };
vector<char> list_3 = { 'c', 'a' };

auto res = Union( list_1, list_2, list_3);


Теперь хотелось бы добавить версию функции , которая получала бы еще предикат или ламбду для сравнения
например :

auto res = Union( list_1, list_2, list_3, cmp());


Но не могу понять как теперь определить сигнатуру функции Union , чтобы автоматически могла выводить тип предиката после переменного количества параметров
Что вроде такого

template<typename T, typename... Ts , typename Pred>
    T Union(const T& c1, const Ts&... rest , Pred pred)
{
..
}


Таким образом чтобы цчтобы мозжно было вызывать :
auto res = Union( list_1, list_2, list_3); — с компаратором по умолчанию
и
auto res = Union( list_1, list_2, list_3, cmp());


Спасибо
Re: Вывод типа шаблонного параметра функции
От: rg45 СССР  
Дата: 18.03.20 09:30
Оценка: 1 (1) +1
Здравствуйте, Alex34, Вы писали:



A>Но не могу понять как теперь определить сигнатуру функции Union , чтобы автоматически могла выводить тип предиката после переменного количества параметров

A>Таким образом чтобы цчтобы мозжно было вызывать :
A> auto res = Union( list_1, list_2, list_3); — с компаратором по умолчанию
A>и
A>auto res = Union( list_1, list_2, list_3, cmp());

Я бы назвал функцию, принимающую предикат, как-нибудь по-другому (UnionIf, например) и поместил бы предикат в начало списка формальных параметров.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 18.03.2020 9:34 rg45 . Предыдущая версия .
Re: Вывод типа шаблонного параметра функции
От: andyp  
Дата: 18.03.20 09:40
Оценка: 13 (3)
Здравствуйте, Alex34, Вы писали:

A>Таким образом чтобы цчтобы мозжно было вызывать :

A> auto res = Union( list_1, list_2, list_3); — с компаратором по умолчанию
A>и
A>auto res = Union( list_1, list_2, list_3, cmp());

Можно так как-то. Все проблемы решаются еще одним уровнем абстракции


#include <iostream>
#include <type_traits>
#include <vector>
#include<array>

template<typename... Ts>
struct select_last
{
    template<typename T>
    struct tag
    {
        using type = T;
    };


    using type = typename decltype((tag<Ts>{}, ...))::type;
};

struct MyPred {};

template<typename Pred>
struct is_predicate : std::false_type {};

template<>
struct is_predicate<MyPred> : std::true_type {};

template<typename T, typename... Ts>
std::enable_if_t<!is_predicate<typename select_last<Ts...>::type>::value, T>
union_helper(Ts... args) 
{
//generic case with default predicate
    std::cout << "generic case" << std::endl;
    return T{};
}

template<typename T, typename... Ts>
std::enable_if_t<is_predicate<typename select_last<Ts...>::type>::value, T>
union_helper(Ts... args)
{
    //case with last predicate
    std::cout << "predicate case" << std::endl;
    return T{};
}

template<typename T, typename... Ts>
T Union(const Ts&... args)
{
    return union_helper<T>(args...);    
};


int main() 
{
    MyPred p{};
    Union<int>(std::vector<int>{}, std::array<int,10>{}, p);
    Union<int>(std::vector<int>{}, std::array<int, 10>{}, std::array<int, 20>{});

}


https://godbolt.org/z/czTRV9
Re[2]: Вывод типа шаблонного параметра функции
От: rg45 СССР  
Дата: 18.03.20 09:47
Оценка: 1 (1)
Здравствуйте, andyp, Вы писали:

A>Можно так как-то. Все проблемы решаются еще одним уровнем абстракции


Технически можно, конечно, нужно ли — вот вопрос. Семантически это другая функция. И с практической точки зрения — существует ли хоть одна причина, по которой эти функции должны иметь одно имя?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 18.03.2020 9:48 rg45 . Предыдущая версия .
Re[3]: Вывод типа шаблонного параметра функции
От: andyp  
Дата: 18.03.20 10:04
Оценка: 1 (1)
Здравствуйте, rg45, Вы писали:

R>Технически можно, конечно, нужно ли — вот вопрос. Семантически это другая функция. И с практической точки зрения — существует ли хоть одна причина, по которой эти функции должны иметь одно имя?


Я бы тоже отдельные семейства сделал По принципу наименьшего удивления по сравнению с стл.

А так, если в философию ударяться, такие вещи требуют имхо лютого ограничения параметров шаблонов, чтобы быть удобными и безопасными для клиента. Это приводит к тоннам кода, вычисляющего воздух, на стороне либы. Если это вызывается в паре мест, я бы вообще такое не писал
Re[2]: Вывод типа шаблонного параметра функции
От: sergii.p  
Дата: 18.03.20 18:00
Оценка:
Здравствуйте, andyp, Вы писали:

A>

A>struct MyPred {};

A>template<typename Pred>
A>struct is_predicate : std::false_type {};

A>template<>
A>struct is_predicate<MyPred> : std::true_type {};

A>


так в случае лямбды этот механизм не работает. Лучше уже поменять местами параметры, как предложили ранее. Или проверять последний параметр на наличие методов begin и end. И если false, то тогда уже делать предположение, что передан предикат, а не коллекция.
Re[3]: Вывод типа шаблонного параметра функции
От: andyp  
Дата: 18.03.20 18:40
Оценка:
Здравствуйте, sergii.p, Вы писали:


SP>так в случае лямбды этот механизм не работает. Лучше уже поменять местами параметры, как предложили ранее. Или проверять последний параметр на наличие методов begin и end. И если false, то тогда уже делать предположение, что передан предикат, а не коллекция.


В с++20 есть concept std::predicate. Надо чтобы вызывалось и возвращало bool. Можно свой дискриминатор типов похожим образом замутить. То, что я привел — просто набросок
Re: Вывод типа шаблонного параметра функции
От: kov_serg Россия  
Дата: 18.03.20 19:05
Оценка:
Здравствуйте, Alex34, Вы писали:

A>Доброе времжя суток и здоровья в эти непростые дни


A>Пишу библиотеку , которая реализует функции математического редактора Wolfram Mathematica.

...
A>auto res = Union( list_1, list_2, list_3, cmp());
...
А как вы планируете объединять не упорядоченные и бесконечные множества?
Re[4]: Вывод типа шаблонного параметра функции
От: sergii.p  
Дата: 19.03.20 07:26
Оценка:
Здравствуйте, andyp, Вы писали:

A>В с++20 есть concept std::predicate. Надо чтобы вызывалось и возвращало bool


так ему надо передать принимаемые типы (а у нас их нет). Просто сказать ему, что нужен абы какой оператор(), который возвращает bool, не получиться.
Re[5]: Вывод типа шаблонного параметра функции
От: rg45 СССР  
Дата: 19.03.20 07:36
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>так ему надо передать принимаемые типы (а у нас их нет). Просто сказать ему, что нужен абы какой оператор(), который возвращает bool, не получиться.


Ну почему же нет. У нас есть входные списки, которые нужно объединить. Из них можно вытащить и тип элемента. А заодно проверить их типовую совмтестимость.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Вывод типа шаблонного параметра функции
От: andyp  
Дата: 19.03.20 10:17
Оценка: 3 (1)
Здравствуйте, sergii.p, Вы писали:

SP>так ему надо передать принимаемые типы (а у нас их нет). Просто сказать ему, что нужен абы какой оператор(), который возвращает bool, не получиться.


есть идея использовать class template argument deduction для std::function как-то так

#include <iostream>
#include <type_traits>
#include <functional>

template<typename Pred>
using ret_ftn_type =  typename decltype(std::function{std::declval<Pred>()})::result_type;

template<typename Pred>
bool is_bool_ret_v = std::is_same_v<ret_ftn_type<Pred>, bool>;

template<typename Pred, typename Enabler = void>
struct is_predicate : std::false_type {};

template<typename Pred>
struct is_predicate<Pred, typename std::enable_if_t<is_bool_ret_v<Pred>>> : std::true_type {};

auto predicate = [](int v){return true;};
auto not_a_predicate = [](int v){return 10;};


int main() 
{

std::cout << (int)(is_predicate<decltype(predicate)>::value) << std::endl;
std::cout << (int)(is_predicate<decltype(not_a_predicate)>::value) << std::endl;

}


Но что-то не работает. Где-то косячу
Отредактировано 19.03.2020 10:17 andyp . Предыдущая версия .
Re[6]: Вывод типа шаблонного параметра функции
От: rg45 СССР  
Дата: 19.03.20 11:02
Оценка:
Здравствуйте, andyp, Вы писали:

A>Но что-то не работает. Где-то косячу


http://coliru.stacked-crooked.com/a/9f0ab79a6a6a8cb8

template <typename T, typename Pred, typename = bool>
struct is_predicate_of : std::false_type {};

template <typename T, typename Pred>
struct is_predicate_of<T, Pred, std::decay_t<decltype(std::declval<Pred>()(std::declval<T>()))>> : std:: true_type {};


Ну а тип параметра предиката вытаскиваем уже из типов входных списков.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 19.03.2020 11:11 rg45 . Предыдущая версия . Еще …
Отредактировано 19.03.2020 11:04 rg45 . Предыдущая версия .
Re[7]: Вывод типа шаблонного параметра функции
От: andyp  
Дата: 19.03.20 11:14
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну а тип параметра предиката вытаскиваем уже из типов входных списков.


Как раз была идея не вытаскивать, а чтобы он автоматически вывелся.

PS Ну т.е. понять, что это invokable, возвращающий bool.

std::function f([](int){return false;});


ведь работает
Отредактировано 19.03.2020 11:22 andyp . Предыдущая версия .
Re[8]: Вывод типа шаблонного параметра функции
От: rg45 СССР  
Дата: 19.03.20 11:22
Оценка:
Здравствуйте, andyp, Вы писали:

R>>Ну а тип параметра предиката вытаскиваем уже из типов входных списков.

A>Как раз была идея не вытаскивать, а чтобы он автоматически вывелся.

А, мы уже от практических потребностей к этюдам перешли? Ну хорошо:

http://coliru.stacked-crooked.com/a/d57523b3e99490f8

struct AnyType
{
    template <typename T>
    operator T() const;
};

template <typename T, typename = bool>
struct is_predicate : std::false_type {};

template <typename T>
struct is_predicate<T, std::decay_t<decltype(std::declval<T>()(AnyType()))>> : std:: true_type {};
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 19.03.2020 11:24 rg45 . Предыдущая версия .
Re[9]: Вывод типа шаблонного параметра функции
От: andyp  
Дата: 19.03.20 11:31
Оценка: 9 (1)
Здравствуйте, rg45, Вы писали:

R>А, мы уже от практических потребностей к этюдам перешли?


Ну да У std::function есть конструктор, жрущий invokable. Типы аргументов оно само должно вывести при инстанциировании. Остается проверить тип, возвращаемый функцией. Я немного подредактировал предыдущий ответ пока ты отвечал.
Re[10]: Вывод типа шаблонного параметра функции
От: rg45 СССР  
Дата: 20.03.20 08:23
Оценка: 12 (1)
Здравствуйте, andyp, Вы писали:

A>Ну да У std::function есть конструктор, жрущий invokable. Типы аргументов оно само должно вывести при инстанциировании. Остается проверить тип, возвращаемый функцией. Я немного подредактировал предыдущий ответ пока ты отвечал.


Да, эта идея мне нравится. Получается органично и компактно:

http://coliru.stacked-crooked.com/a/e397bfcb155981b0

template <typename T>
using is_predicate = std::is_same<bool, std::decay_t<typename decltype(std::function(std::declval<T>()))::result_type>>;


Не очень привычна пока еще эта фишка, когда параметры шаблона класса выводятся по фактическим параметрам конструктора. Будем надеяться, что подобные конструкции не будут вызывать трудностей у компиляторов.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[11]: Вывод типа шаблонного параметра функции
От: andyp  
Дата: 20.03.20 08:34
Оценка:
Здравствуйте, rg45, Вы писали:

R>Да, эта идея мне нравится. Получается органично и компактно:


Спасибо! У меня без decay_t не работало.
Re[12]: Вывод типа шаблонного параметра функции
От: rg45 СССР  
Дата: 20.03.20 08:39
Оценка: 12 (1)
Здравствуйте, andyp, Вы писали:

A>Спасибо! У меня без decay_t не работало.


Ай блин, чаинки еще не утонули не все так безоблачно. Для произвольного невызываемого типа мой вариант валится по ошибке компиляции, вместо того, чтобы вычислиться в false. Придется все-таки прикрутить SFINAE:

http://coliru.stacked-crooked.com/a/131b84dd7522caf9

template <typename T, typename = bool>
struct is_predicate : std::false_type {};

template <typename T>
struct is_predicate<T, std::decay_t<typename decltype(std::function(std::declval<T>()))::result_type>> : std::true_type {};


Но и так неплохо, по-моему.
--
Не можешь достичь желаемого — пожелай достигнутого.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.