LINUX.ORG.RU

Получить тип возвращаемого значения из лямбды

 


3

2

Нюанс в том, что незивестен точный тип лямбды, они приходят из пользовательского кода, следовательно мне неизвестны типы и количество аргументов, которые функтор ожидает, т.е. я не могу использовать invoke_result<>. Нужно заглянуть в класс, схватить любой оператор() и вернуть тип возврата. Получилось такое:


template <typename T>
struct Return_type;
template <typename R, typename... Args>
struct Return_type<R(Args...)> {using type = R;};
template <typename R, typename Q, typename... Args>
struct Return_type<R (Q::*)(Args...)const> {using type = R;};
template <typename R, typename Q, typename... Args>
struct Return_type<R (Q::*)(Args...)> {using type = R;};

struct A{
	struct V {
		double operator()(int t)const {return 5;}
	};
};

int main() {
	auto l = [](double a, int e)mutable{return 2;};
	auto l2 = [](int a){;return 2;};
	Return_type<decltype(&decltype(l)::operator())>::type a;
	Return_type<decltype(&decltype(l2)::operator())>::type a2;
	Return_type<decltype(&A::V::operator())>::type a3;


	auto l3 = [](auto b){;return 2;};
	Return_type<decltype(&decltype(l3)::operator())>::type a4;

	return 0;
}

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

★★

Решаема ли задача в случае с l3?

Вряд ли. Чтобы вывести возвращаемый тип, надо посмотреть все возможные специализации (и не важно, что они все возвращают одно и то же).

Не проще ли указать возвращаемый тип лямбды [](auto) -> int { /* ... */ }? Тогда должно работать

anonymous
()
Ответ на: комментарий от anonymous

Вряд ли. Чтобы вывести возвращаемый тип, надо посмотреть все возможные специализации (и не важно, что они все возвращают одно и то же).

Я в общем-то к тому же склоняюсь. Но чтобы вопрос закрыть - решил посоветоваться.

Не проще ли указать возвращаемый тип лямбды -> int { /* … */ }? Тогда должно работать

Не, так тоже не будет, в лямбде будет шаблонный оператор().

kvpfs ★★
() автор топика

мне не понятно — если ты собираешься использовать проброшенный функтор для проброса дальше — то зачем знать тип?
а в случае если собираешься использовать результат внутри своей реализации — тебе же надо будет четко указать, какие типы функторов ты ожидаешь принимать.

safocl ★★
()

но не знаю как (и возможно ли вообще) получить тип от лямбды, внутри которой шаблонный оператор()

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

Return_type<decltype(&decltype(l3<TYPE>)::operator())>::type a4;

или
Return_type<decltype(&decltype(l3)::operator<TYPE>())>::type a4;

safocl ★★
()
Последнее исправление: safocl (всего исправлений: 1)
Ответ на: комментарий от safocl

Да, всё это звучит странно. Схема примерно такая: формируется визитор с перегруженными операторами(), который потом отправляется в std::visit, набор типов хоть и конечный, но не все перегрузки могут быть представлены, также в теории можно всунуть вообще левую перегрузку. Каждая перегрузка возвращает команду из энума, и вот если случайно ошибиться и вернуть инт вместо энума, то на выходе будет десятиэтажная ошибка с проходом по всем закоулкам стд. Ну и чтобы людей не пугать, хочу сделать концепт над конструктором визитора для проверки возвращаемого типа из лямбды, ну а для этого как раз надо получить return тип, а invoke_result или decltype(fn(…)) здесь не заюзать.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

над конструктором

Что я говорю, там обычный агрегат, не над конструктором, а так примерно:

template <typename... T>
requires (return_command_enum_concept<T> && ...)
struct Visitor : T..., Base
{
   ...
};
kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

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

safocl ★★
()
Ответ на: комментарий от safocl

Мне приходит визитор без аргументов, потом уже std::visit сама сопостовляет фактически хранимый тип с набором () в визиторе и вызывает соответствующее. Давай на примерах, как убедиться, что все лямбды в визиторе возвращают int?

auto default_operator = [](auto &&) {return 7;};
template<class... Ts>
struct Visitor :  Ts..., decltype(default_operator) {
	using Ts::operator()...;
	using decltype(default_operator)::operator();
};
template<class... Ts> Visitor(Ts...) -> Visitor<Ts...>;

int main() {
	Visitor v{
		[](int) {return 1;},
		[](int, double) {return 1;},
		[](int *) {return 2;},
		[](const char*) {return 3.0;} // not int!!!!
	};
	std::variant<int, double*> a{10};
	std::visit(v, a);
	
	return 0;
}
kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Вообще можно сделать что-нибудь вроде

#define Types int, double, char
using v_t = variant<Types>;
using t_t = tuple<Types>;

И потом повызывать через invoke_result функторы из визитора с каждым типом из tuple (с variant’a вроде тип не достать) и посмотреть на возвращаемый тип. Только всё это надо упаковать в коцепт. Поупражняюсь сегодня с этой задачей.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Это не С++ - это какая-то шиза. Очевидно, что в полиморфной логики без контекста невозможно получить тип. Есть контекст - decltype(f(…)). Нету - мимо.

Далее, очевидно, что в полиморфной логике не может быть сопоставления типов - это какие-то си с классами методички.

anonymous
()
Ответ на: комментарий от kvpfs

(с variant’a вроде тип не достать)

С чего?

И потом повызывать через invoke_result функторы из визитора с каждым типом из tuple

Изучи С++, а не какую-то убогую на него си с классами жава-породию.

Только всё это надо упаковать в коцепт.

Ненужен тут никакой концепт. Он нужен для ограничения полиморфизма.

anonymous
()
Ответ на: комментарий от anonymous

Дорогой друг, не разводи здесь срач, пожалуйста. Давай без всех этих любезностей. Всей это проверки я могу не делать вовсе, но это интерфейсная часть либы и я написал, что цель - получить внятную ошибку в несколько строк при несоответствии типа лямбды ожидаемому. Ошибка и так будет, но будет она на полтора экрана, зачем выдавать эту портянку?

kvpfs ★★
() автор топика

Это ты еще хорошо отделался. Применяю похожую практику при проверке аргументов через концепты. Там как раз лямбды подсовываются. Только при вызове ограничений концептов я еще эти лямбды с начала преобразую явно к std::function, а потом прогоняю через похожие шаблоны, которые уже умеют распознавать std::function.

Ну, не раст! Чего сделаешь?) Там было бы все явно.

anonymous
()
Ответ на: комментарий от kvpfs

[](const char*) {return 3.0;} // not int!!!!

так тут как раз decltype отработает нормально.

safocl ★★
()
Ответ на: комментарий от kvpfs

В общем такое:

#include <variant>
#include <type_traits>
using namespace std;

#define VARIANT_TYPES int, double*, const char*
using variant_t = variant<VARIANT_TYPES>;

auto default_operator = [](auto &&) {return 7;};
template <typename... Fn>
struct Visitor_base :  Fn..., decltype(default_operator) {
		using Fn::operator()...;
		using decltype(default_operator)::operator();
};
template <typename Fn, typename... Vtypes>
concept Check_for_int_return = (is_same_v<invoke_result_t<Fn, Vtypes>, int> && ...);

template <typename... Fn>
requires Check_for_int_return<Visitor_base<Fn...>, VARIANT_TYPES>
struct Visitor : Visitor_base<Fn...> {};

template<class... Fn> Visitor(Fn...) -> Visitor<Fn...>;

int main () {
	Visitor vis{ 
		[](auto b) {return 4;},
		[](int) {return 3;},
		[](double*) {return 3;},
		[](const char*) {return 3;},
		[](auto a) {return 4;}
	};
}

Честная проверка каждого возможного вызова из std::visit.

Но тут другое печально - компилятору в выхлопе дают какую-то муть неясную, ну хотя бы первой строкой надо писать «Смотри, с концептом беда», а сейчас можно обнаружить упоминания об этом ближе к концу. Шланг хоть кратким старается быть, ГЦЦ же вообще жалуется обо всём, что наболело.

kvpfs ★★
() автор топика
Ответ на: комментарий от safocl

а как тут еще сделать?

И действительно, как посчитать учёт стеклотары в опердне стеклоприёмного пункта без концептов и loop-unroll'инга?

P.S. В младших классах с++ школы проходят наследование и виртуальные методы.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

ну не везде это все пригодно, да и наследование не сможет тут помочь с данной задачей...
тут же нет объявления необходимой абстракции которой все пользователи должны соответствовать...

safocl ★★
()
Ответ на: комментарий от LamerOk

Вместо дешевого статического связывания, ты предлагаешь мне впердолить сюда вот всё это, да ещё и во внешнем коде хранить объект, или созавать в куче, не, спасибо, я как-нибудь так покостылю.

На самом деле лучшей альтернативой, чем твоя - это простые юнионы. Ну там свои за и против. В общем так решил в итоге, да и надо где это упражняться в новых плюсах.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Тебе царь уже отписал, что ты ерундой занимаешься. Ты написал кучу лапши просто потому, что не научился читать выхлоп компилятора. И беда в том, что читать проще не стало, но добавилась груда движущихся частей, каждая из которых будет добавлять пару строчек в контекст ошибки.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от kvpfs

Вместо статического связывания,

А ты у мамы не очень умный.

Вместо дешевого статического связывания,

Если ты пребываешь в блаженных иллюзиях, что накостыленная тобою шизофазия - зерокост, то см. комментарий выше.

LamerOk ★★★★★
()
Последнее исправление: LamerOk (всего исправлений: 1)
Ответ на: комментарий от LamerOk

Тсссс. Не мешай мне наблюдать за развитием ситуации «макака не может отказаться от банана» в контексте развития языка С++.

Мне нравится смотреть, как погромисты хватаются за всю херню, которую тащат в стандарт и около. Хватаются несмотря на то, что сами уже не могут понять, что и зачем они пишут.

Просто наблюдай, это завораживает.

anonymous
()
Ответ на: комментарий от Siborgium

Ты написал кучу лапши просто потому

Какой лапшы? Один концепт? Это не SFINAE портянка какая-то, а визитор вообще стандартная заготовка из справочника. Тут моего «оригинального» кода 5-7 строк ровно.

каждая из которых будет добавлять пару строчек в контекст ошибки.

Это не так, как раз ошибка будет на самом первом этапе, когда визитор только создается, а не где-то в дебрях когда вызов совершается. Лично у меня больше вопросов к ГЦЦ - почему в его выводе в 3 раза больше лапши, чем в шланге.

kvpfs ★★
() автор топика
Ответ на: комментарий от anonymous

Ну если тебе концепты не нравятся, то ты, наверное, никогде раньше SFINAE портянку не писал. Пиши как раньше и страдай.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs
  1. Тебе не нужны концепты и SFINAE. Тебе просто нужен визитор как в примере, больше ничего.
  2. Ты неправильно используешь концепты. Я на 99% уверен, что лапша в твоем выводе вызвана именно этим. То, что ты хочешь сделать, делается через static_assert, а не через концепты.
Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Ты неправильно используешь концепты. Я на 99% уверен, что лапша в твоем выводе вызвана именно этим. То, что ты хочешь сделать, делается через static_assert, а не через концепты.

Да, возможно это разумно, попробую на них

kvpfs ★★
() автор топика
Ответ на: комментарий от Siborgium

Да, со статик_ассертом сообщения об ошибках полностью устраивают, в самом начале, с пояснением, спасибо за наводку, я как-то о них не подумал.

#include <variant>
#include <type_traits>
using namespace std;

#define VARIANT_TYPES int, double*, const char*
using variant_t = variant<VARIANT_TYPES>;

auto default_operator = [](auto &&) {return 7;};
template <typename... Fn>
struct Visitor_base :  Fn..., decltype(default_operator) {
		using Fn::operator()...;
		using decltype(default_operator)::operator();
};
template <typename Fn, typename... Vtypes>
concept Check_for_int_return = (is_same_v<invoke_result_t<Fn, Vtypes>, int> && ...);

template <typename... Fn>
struct Visitor : Visitor_base<Fn...> {
	static_assert(Check_for_int_return<Visitor_base<Fn...>, VARIANT_TYPES>,
			"return type of Visitor function must be int");
};

template<class... Fn> Visitor(Fn...) -> Visitor<Fn...>;
...
kvpfs ★★
() автор топика

Ах да, на тему С++. Ведь этот мусор заменяется на: https://godbolt.org/z/E94bc68sn

Ну и в целом стоит обновить методичку, Как минимум с этим: «return 0;» - это совсем позорище.

Return_type

Это туда же.

Как уже было сказано до - такое с полиморфными сущностями не работает. Организуй контекст вызова полностью, либо это невозможно. Да и ненужно это, о чём уже так же было сказано.

anonymous
()
Ответ на: комментарий от kvpfs

Твоя проблема в том, что ты заучил пару трюков и суёшь их везде без какого-либо понимания.

Какой концепт, какая прочая нелепая чушь. Какое «Нельзя получить типы из варианта»? Всё это полная херня.

template<typename ... Ts> f(variant<Ts...>) - вот как получить типы из варианта.

anonymous
()
Ответ на: комментарий от kvpfs

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

Должно быть когда в дебрях. Если ты не можешь этого понять - тебе в жаву. И оно итак всегда будет в дебрях.

Лично у меня больше вопросов к ГЦЦ - почему в его выводе в 3 раза больше лапши, чем в шланге.

Потому что ты жертва пропаганды. Количество лапши определяет качество ошибки. ЦА не боты подобные тебе, а люди, которым нужна информация. И чем больше информации - тем лучше.

anonymous
()

Какой все таки замечательный язык программирования C++.
Все проще паренной репы.

anonymous
()
Ответ на: комментарий от anonymous
constexpr struct {
  template<typename T> operator T &&() const;
} any;

Да, это годный трюк, я об этом не подумал, спасибо, хоть и язвишь ты шибко много.

Как минимум с этим: «return 0;» - это совсем позорище

Не понял

kvpfs ★★
() автор топика
Ответ на: комментарий от anonymous

template<typename … Ts> f(variant<Ts…>) - вот как получить типы из варианта.

Знаю, но с шаблонами структур это не работает.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Да, это годный трюк, я об этом не подумал, спасибо, хоть и язвишь ты шибко много.

У тебя проблема не с трюком, а в том, что ты в принципе не понимаешь как работает этот язык.

is_same_v<invoke_result_t<Fn, Vtypes>, int> - вот пруф. Не пытайся меня обманывать.

Не понял

Что тебе непонятно? Наличие return 0 в любом коде признак того, что человек даже базовой методички по языку не читал.

anonymous
()
Ответ на: комментарий от anonymous

Должно быть когда в дебрях. Если ты не можешь этого понять - тебе в жаву. И оно итак всегда будет в дебрях.

Ты видимо не можешь понять, что интерфейс библиотеки, и юзеру нафиг эти кишки не нужны. Не знаю куда с этим.

kvpfs ★★
() автор топика
Ответ на: комментарий от anonymous

Ок. Показывай пример того, как ты инстанцируешь шаблонную структуру с двумя parameter pack’ами вида:

template <typename... T, typename... U>
kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Ты видимо не можешь понять, что интерфейс библиотеки, и юзеру нафиг эти кишки не нужны. Не знаю куда с этим.

Это ты не можешь понять. Ведь явно балабол с парой трюков лучше знает, что и как должно быть.

К тому же ты перепутал методичку. Это у тебя кишки, а юзеры подобные тебе здесь ненужны. Поэтому я тебе посоветовал взять то, что достойно тебя - пхп/жаву.

C++ как раз таки и не предполагает кишок, а вот у тебя они есть. Ошибка должна быть максимально близко с пользовательскому коду, должна требовать явных инвариантов от него.

А не какую-то неведомую херню непонятно откуда взявшуюся и чем обусловленную.

anonymous
()
Ответ на: комментарий от kvpfs

Ещё раз, зачем ты, будучи зелёным балаболом с парой трюков, пытаешься со мною спорить? Какие шаблонную структуру, какую инстанцируешь? Что ты несёшь, трепло? Ты засыпалось на вранье «нельзя получить типы», когда их получить можно и это очевидно любому, кто что-то хотя бы минимальное об этом языке знает.

Поэтому либо показываешь какие типы из варианта нельзя вытащить таким образом, либо не пытаешься спорить.

anonymous
()
Ответ на: комментарий от anonymous

Я когда писал, то была необходимость таскать два параметр пака, отсюда возник пост «из варианта типы не вытянуть», т.к. один пакет с функторами. Потом сделал иначе и функторы все ушли в одну сущность. Так что в теории теперь можно переписать без дефайна и запросить типы из самого варианта.

А вообще, если хочешь удивить, реши задачу красивее и покажи, чего ты здесь многословишь то.

kvpfs ★★
() автор топика
Ответ на: комментарий от anonymous

Ты засыпалось на вранье «нельзя получить типы», когда их получить можно и это очевидно любому, кто что-то хотя бы минимальное об этом языке знает.

Вообще-то очевидно, что я склонен говорить в контексте своей задачи, трепло.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Я когда писал, то была необходимость таскать два параметр пака, отсюда возник пост «из варианта типы не вытянуть», т.к. один пакет с функторами. Потом сделал иначе и функторы все ушли в одну сущность. Так что в теории теперь можно переписать без дефайна и запросить типы из самого варианта.

А ну типичное «я не я корова не моя», да, да. Какой пакет, какие функторы. Ты сообщал, что вытащить нельзя. Показывай тот случай, в котором показанное мною работать не будет. Бегом.

А вообще, если хочешь удивить, реши задачу красивее и покажи, чего ты здесь многословишь то.

Зачем мне тебе что-то решать? Мало того, что здесь нет задачи. И уж тем более мне ничего никому доказывать ненужно. Для любого я уже её решил. А писать код за эникея мне не упало.

anonymous
()
Ответ на: комментарий от kvpfs

Вообще-то очевидно, что я склонен говорить в контексте своей задачи, трепло.

Ну показывай задачу, показываешь вариант, суёшь в него что угодно и показываешь как это не работает. Чего ты сразу поплыл?

anonymous
()
Ответ на: комментарий от kvpfs

Да, работает c двумя паками:

template <typename... T>
struct C;
template <typename... T, typename... U>
struct C<variant<T...>, variant<U...>> {
};

хз почему я так думал, признаю, ошибался.

kvpfs ★★
() автор топика
Ответ на: комментарий от Lrrr

return 0; в конце main писать не обязательно…

Если только не подключал всякие скам библиотеки типа Qt, которые делают дефайн на main :)

Тогда обязательно.

https://github.com/qt/qtbase/blob/b10e4e846e7b1a7b4c9c7cb7b4ef1081e22f2354/src/entrypoint/qtentrypoint_win.cpp#L63-L102

fsb4000 ★★★★★
()
Ответ на: комментарий от anonymous

мде... канечна же куда лучше писать на си++11 ... (нет)

safocl ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.