Is it possible to obtain the type of the first parameter of a lambda?

Hi,

I have the following lambda:

1
2
3
auto selectViaCategory = []( const std::string& categoryName,  const shared_ptr<CreditCardStatementLines>& lines) {
	return lines->m_category->m_name == categoryName;
};


Can I write code that extracts the type of the first parameter of a lambda like the one above? I would like to distinguish it from say this other lambda:

1
2
3
auto selectViaDate = [](date::day_point dp, const shared_ptr<CreditCardStatementLines>& lines) {
	return lines->m_lineDate == dp;
};


Any thoughts?

Thanks
Juan
This would only be possible for non-polymorphic lambdas (such as the ones you are showing), since otherwise there is no specific type to extract. For a non-polymorphic lambda, you can make a pointer to its call operator, and decompose that pointer's type. For example, using the old boost facilities: using p1 = at<parameter_types<decltype(&decltype(selectViaCategory)::operator())>, int_<1>>::type;, or using something less cumbersome, but it's rare that this kind of decomposition is actually needed. What exactly needs to distinguish these lambdas?

Hi,

I have some code that is redundant on the first parameter of the lambda and that's what I want deduced. In other words, I have this code:

 
processingObj.filter<date::day_point>(selectViaDate);


Now the type date::day_point is also the first parameter of the lambda selectViaDate. So it is redundant and could lead to inconsistencies if I use a different type (by mistake). If I could figure out the first parameter of the passed lambda I have a more foolproof design and better code... So I want to be able to call:

 
processingObj.filter(selectViaDate);


and figure out the type from the lambda.

Is that clear?

Juan
One question Cubbi:

why do we need a pointer to the lambda's call operator instead of just the call operator?

In other words, why does this not work:

 
 using p1 = at<parameter_types<decltype(decltype(selectViaCategory)::operator())>, int_<1>>::type;


BTW, your solution works beautifully!!


Thansk,
Juan
Last edited on
"decltype(selectViaCategory)::operator()" is effectively "T::m", where m is non-static member function. The standard prohibits this (5.1.4[expr.prim.id]): you can decltype a non-static data member, but not a non-static member function.

For processingObj.filter, why not make that filter decide? It seems you want a function object that can be called with string/date, not specifically a monomorphic lambda whose first parameter is string/date.

Something like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <string>
#include <iostream>
#include <memory>
#include <chrono>
#include <type_traits>
using namespace std::literals;

struct CreditCardStatementLines { std::string m_category; std::chrono::seconds m_lineDate; };

auto selectViaCategory = []( const std::string& categoryName,  const std::shared_ptr<CreditCardStatementLines>& lines) {
	return lines->m_category == categoryName;
};

auto selectViaDate = [](std::chrono::seconds dp, const std::shared_ptr<CreditCardStatementLines>& lines) {
	return lines->m_lineDate == dp;
};

struct Processing {
    template<class F>
    auto filter(F&& f) -> std::void_t<decltype(f(""s, nullptr))> { std::cout << " string overload\n"; }
    template<class F, class = void>
    auto filter(F&& f) -> std::void_t<decltype(f(0s, nullptr))> { std::cout << " time_point overload\n"; }
} processingObj;

int main()
{
    processingObj.filter(selectViaCategory);
    processingObj.filter(selectViaDate);
}
Last edited on
...or
1
2
3
4
    template<class F, decltype(std::declval<F>()(""s, nullptr))* = nullptr>
    void filter(F&& f) { std::cout << " string overload\n"; }
    template<class F, decltype(std::declval<F>()(0s, nullptr))* = nullptr>
    void  filter(F&& f) { std::cout << " time_point overload\n"; }

there are plenty of ways to arrange those
Topic archived. No new replies allowed.