Is it possible to static_assert that a lambda is not generic?





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







10















I implemented a Visit function (on a variant) that checks that the currently active type in the variant matches the function signature (more precisely the first argument). Based on this nice answer.
For example



#include <variant>
#include <string>
#include <iostream>

template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));

std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data)) {
std::cerr<< "alternative mismatchn";
return;
}
v(std::get<Arg1>(data));
}
int main(){
Visit((const int& i){std::cout << i << "n"; });
Visit((const std::string& s){std::cout << s << "n"; });
// Visit((auto& x){}); ugly kabooom
}


This works, but it explodes with a user unfriendly compile time error when users passes a generic (e.g. (auto&){}) lambda. Is there a way to detect this and give nice static_assert() about it?
Would also be nice if it worked with function templates as well, not just with lambdas.



Note that I do not know what possible lambdas do, so I can not do some clever stuff with Dummy types since lambdas may invoke arbitrary functions on types.
In other words I can not try to call lambda in 2 std::void_t tests on int and std::string and if it works assume it is generic because they might try to call .BlaLol() on int and string.










share|improve this question




















  • 1





    What if the functor has an overloaded operator()? Visiting is also very commonly performed with overloaded functors (see example 4 here), do those have to be forbidden (or have to work)?

    – Max Langhof
    Apr 3 at 7:35













  • I think that is too hard to handle, but if it can be done that would be nice... so it is optional, not required.

    – NoSenseEtAl
    Apr 3 at 8:16


















10















I implemented a Visit function (on a variant) that checks that the currently active type in the variant matches the function signature (more precisely the first argument). Based on this nice answer.
For example



#include <variant>
#include <string>
#include <iostream>

template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));

std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data)) {
std::cerr<< "alternative mismatchn";
return;
}
v(std::get<Arg1>(data));
}
int main(){
Visit((const int& i){std::cout << i << "n"; });
Visit((const std::string& s){std::cout << s << "n"; });
// Visit((auto& x){}); ugly kabooom
}


This works, but it explodes with a user unfriendly compile time error when users passes a generic (e.g. (auto&){}) lambda. Is there a way to detect this and give nice static_assert() about it?
Would also be nice if it worked with function templates as well, not just with lambdas.



Note that I do not know what possible lambdas do, so I can not do some clever stuff with Dummy types since lambdas may invoke arbitrary functions on types.
In other words I can not try to call lambda in 2 std::void_t tests on int and std::string and if it works assume it is generic because they might try to call .BlaLol() on int and string.










share|improve this question




















  • 1





    What if the functor has an overloaded operator()? Visiting is also very commonly performed with overloaded functors (see example 4 here), do those have to be forbidden (or have to work)?

    – Max Langhof
    Apr 3 at 7:35













  • I think that is too hard to handle, but if it can be done that would be nice... so it is optional, not required.

    – NoSenseEtAl
    Apr 3 at 8:16














10












10








10


2






I implemented a Visit function (on a variant) that checks that the currently active type in the variant matches the function signature (more precisely the first argument). Based on this nice answer.
For example



#include <variant>
#include <string>
#include <iostream>

template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));

std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data)) {
std::cerr<< "alternative mismatchn";
return;
}
v(std::get<Arg1>(data));
}
int main(){
Visit((const int& i){std::cout << i << "n"; });
Visit((const std::string& s){std::cout << s << "n"; });
// Visit((auto& x){}); ugly kabooom
}


This works, but it explodes with a user unfriendly compile time error when users passes a generic (e.g. (auto&){}) lambda. Is there a way to detect this and give nice static_assert() about it?
Would also be nice if it worked with function templates as well, not just with lambdas.



Note that I do not know what possible lambdas do, so I can not do some clever stuff with Dummy types since lambdas may invoke arbitrary functions on types.
In other words I can not try to call lambda in 2 std::void_t tests on int and std::string and if it works assume it is generic because they might try to call .BlaLol() on int and string.










share|improve this question
















I implemented a Visit function (on a variant) that checks that the currently active type in the variant matches the function signature (more precisely the first argument). Based on this nice answer.
For example



#include <variant>
#include <string>
#include <iostream>

template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));

std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data)) {
std::cerr<< "alternative mismatchn";
return;
}
v(std::get<Arg1>(data));
}
int main(){
Visit((const int& i){std::cout << i << "n"; });
Visit((const std::string& s){std::cout << s << "n"; });
// Visit((auto& x){}); ugly kabooom
}


This works, but it explodes with a user unfriendly compile time error when users passes a generic (e.g. (auto&){}) lambda. Is there a way to detect this and give nice static_assert() about it?
Would also be nice if it worked with function templates as well, not just with lambdas.



Note that I do not know what possible lambdas do, so I can not do some clever stuff with Dummy types since lambdas may invoke arbitrary functions on types.
In other words I can not try to call lambda in 2 std::void_t tests on int and std::string and if it works assume it is generic because they might try to call .BlaLol() on int and string.







c++ c++17 variadic-templates template-meta-programming generic-lambda






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Apr 3 at 8:16









max66

39.1k74574




39.1k74574










asked Apr 3 at 6:21









NoSenseEtAlNoSenseEtAl

7,7271677184




7,7271677184








  • 1





    What if the functor has an overloaded operator()? Visiting is also very commonly performed with overloaded functors (see example 4 here), do those have to be forbidden (or have to work)?

    – Max Langhof
    Apr 3 at 7:35













  • I think that is too hard to handle, but if it can be done that would be nice... so it is optional, not required.

    – NoSenseEtAl
    Apr 3 at 8:16














  • 1





    What if the functor has an overloaded operator()? Visiting is also very commonly performed with overloaded functors (see example 4 here), do those have to be forbidden (or have to work)?

    – Max Langhof
    Apr 3 at 7:35













  • I think that is too hard to handle, but if it can be done that would be nice... so it is optional, not required.

    – NoSenseEtAl
    Apr 3 at 8:16








1




1





What if the functor has an overloaded operator()? Visiting is also very commonly performed with overloaded functors (see example 4 here), do those have to be forbidden (or have to work)?

– Max Langhof
Apr 3 at 7:35







What if the functor has an overloaded operator()? Visiting is also very commonly performed with overloaded functors (see example 4 here), do those have to be forbidden (or have to work)?

– Max Langhof
Apr 3 at 7:35















I think that is too hard to handle, but if it can be done that would be nice... so it is optional, not required.

– NoSenseEtAl
Apr 3 at 8:16





I think that is too hard to handle, but if it can be done that would be nice... so it is optional, not required.

– NoSenseEtAl
Apr 3 at 8:16












3 Answers
3






active

oldest

votes


















11















Is there a way to detect this and give nice static_assert about it?




I suppose you can use SFINAE over operator() type.



Follows an example



#include <type_traits>

template <typename T>
constexpr auto foo (T const &)
-> decltype( &T::operator(), bool{} )
{ return true; }

constexpr bool foo (...)
{ return false; }

int main()
{
auto l1 = (int){ return 0; };
auto l2 = (auto){ return 0; };

static_assert( foo(l1), "!" );
static_assert( ! foo(l2), "!" );
}


Instead of a bool, you can return std::true_type (from foo() first version) or std::false_type (from second version) if you want to use it through decltype().




Would also be nice if it worked with function templates as well, not just with lambdas.




I don't think it's possible in a so simple way: a lambda (also a generic lambda) is an object; a template function isn't an object but a set of objects. You can pass an object to a function, not a set of objects.



But the preceding solution should works also for classes/structs with operator()s: when there is a single, non template, operator(), you should get 1 from foo(); otherwise (no operator(), more than one operator(), template operator()), foo() should return 0.






share|improve this answer





















  • 2





    Trying to take the address of operator() was my initial idea as well (hence the comment) but then I somehow got lost in SFINAE on actually calling it. Anyway, here are some extra test cases involving overloaded functors: godbolt.org/z/M319jo

    – Max Langhof
    Apr 3 at 8:32





















4














Yet another simpler option:



#include <type_traits>
...
template <typename V>
void Visit(V v) {
class Auto {};
static_assert(!std::is_invocable<V, Auto&>::value);
static_assert(!std::is_invocable<V, Auto*>::value);
...
}


The Auto class is just an invented type impossible to occur in the V parameters. If V accepts Auto as an argument it must be a generic.



I tested in coliru and I can confirm the solution covers these cases:



Visit((auto x){}); // nice static assert
Visit((auto *x){}); // nice static assert
Visit((auto &x){}); // nice static assert
Visit((auto &&x){}); // nice static assert


I'm not sure if that would cover all the possible lambdas that you don't know which are :)






share|improve this answer


























  • Nice idea ! You may ensure that "Auto" is unique like that : auto a_lambda = (){}; using Auto= decltype(a_lambda);

    – Martin m
    Apr 3 at 9:58













  • @Martinm This seems to be the idiomatic way but does it actually have an advantage over the code in this answer?

    – Konrad Rudolph
    Apr 3 at 10:07











  • Well, I am not sure what happens in the case where an "Auto" class already define somewhere else. In meta-programming context I prefer to be sure that my type cannot be in conflict in some obscure case.

    – Martin m
    Apr 3 at 10:19






  • 4





    This gives both false positives (e.g. Visit((std::any){});) and false negatives (Visit((int, auto){}); or Visit((auto*){});)

    – Barry
    Apr 3 at 11:47













  • Visit((int, auto){}) is not a valid case because of v(std::get<Arg1>(data)); and Visit((auto*){}); is fixed now. I'm not sure if Visit((std::any){}) is one of the cases to be avoided as the question isn't clear enough.

    – olivecoder
    Apr 3 at 12:09





















3














#include <variant>
#include <string>
#include <iostream>

template <class U, typename T = void>
struct can_be_checked : public std::false_type {};

template <typename U>
struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};

template <typename U>
struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};


template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));

std::variant<int, std::string> data="abc";


template <typename V>
void Visit(V v){
if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
{
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data))
{
std::cerr<< "alternative mismatchn";
return;
}
v(std::get<Arg1>(data));
}
else
{
std::cout << "it's a template / auto lambda " << std::endl;
}


}

template <class T>
void foo(const T& t)
{
std::cout <<t << " foo n";
}

void fooi(const int& t)
{
std::cout <<t << " fooi " << std::endl;
}

int main(){
Visit((const int& i){std::cout << i << std::endl; });
Visit((const std::string& s){std::cout << s << std::endl; });
Visit((auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
Visit(foo<int>);

Visit<decltype(fooi)>(fooi);
Visit(fooi);


// Visit(foo); // => fail ugly
}


I don't know if it's you want, but you can, with that static_assert if an auto lambda is passed as parameter.



I think it's not possible to do the same for template function, but not sure.






share|improve this answer
























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55488333%2fis-it-possible-to-static-assert-that-a-lambda-is-not-generic%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    11















    Is there a way to detect this and give nice static_assert about it?




    I suppose you can use SFINAE over operator() type.



    Follows an example



    #include <type_traits>

    template <typename T>
    constexpr auto foo (T const &)
    -> decltype( &T::operator(), bool{} )
    { return true; }

    constexpr bool foo (...)
    { return false; }

    int main()
    {
    auto l1 = (int){ return 0; };
    auto l2 = (auto){ return 0; };

    static_assert( foo(l1), "!" );
    static_assert( ! foo(l2), "!" );
    }


    Instead of a bool, you can return std::true_type (from foo() first version) or std::false_type (from second version) if you want to use it through decltype().




    Would also be nice if it worked with function templates as well, not just with lambdas.




    I don't think it's possible in a so simple way: a lambda (also a generic lambda) is an object; a template function isn't an object but a set of objects. You can pass an object to a function, not a set of objects.



    But the preceding solution should works also for classes/structs with operator()s: when there is a single, non template, operator(), you should get 1 from foo(); otherwise (no operator(), more than one operator(), template operator()), foo() should return 0.






    share|improve this answer





















    • 2





      Trying to take the address of operator() was my initial idea as well (hence the comment) but then I somehow got lost in SFINAE on actually calling it. Anyway, here are some extra test cases involving overloaded functors: godbolt.org/z/M319jo

      – Max Langhof
      Apr 3 at 8:32


















    11















    Is there a way to detect this and give nice static_assert about it?




    I suppose you can use SFINAE over operator() type.



    Follows an example



    #include <type_traits>

    template <typename T>
    constexpr auto foo (T const &)
    -> decltype( &T::operator(), bool{} )
    { return true; }

    constexpr bool foo (...)
    { return false; }

    int main()
    {
    auto l1 = (int){ return 0; };
    auto l2 = (auto){ return 0; };

    static_assert( foo(l1), "!" );
    static_assert( ! foo(l2), "!" );
    }


    Instead of a bool, you can return std::true_type (from foo() first version) or std::false_type (from second version) if you want to use it through decltype().




    Would also be nice if it worked with function templates as well, not just with lambdas.




    I don't think it's possible in a so simple way: a lambda (also a generic lambda) is an object; a template function isn't an object but a set of objects. You can pass an object to a function, not a set of objects.



    But the preceding solution should works also for classes/structs with operator()s: when there is a single, non template, operator(), you should get 1 from foo(); otherwise (no operator(), more than one operator(), template operator()), foo() should return 0.






    share|improve this answer





















    • 2





      Trying to take the address of operator() was my initial idea as well (hence the comment) but then I somehow got lost in SFINAE on actually calling it. Anyway, here are some extra test cases involving overloaded functors: godbolt.org/z/M319jo

      – Max Langhof
      Apr 3 at 8:32
















    11












    11








    11








    Is there a way to detect this and give nice static_assert about it?




    I suppose you can use SFINAE over operator() type.



    Follows an example



    #include <type_traits>

    template <typename T>
    constexpr auto foo (T const &)
    -> decltype( &T::operator(), bool{} )
    { return true; }

    constexpr bool foo (...)
    { return false; }

    int main()
    {
    auto l1 = (int){ return 0; };
    auto l2 = (auto){ return 0; };

    static_assert( foo(l1), "!" );
    static_assert( ! foo(l2), "!" );
    }


    Instead of a bool, you can return std::true_type (from foo() first version) or std::false_type (from second version) if you want to use it through decltype().




    Would also be nice if it worked with function templates as well, not just with lambdas.




    I don't think it's possible in a so simple way: a lambda (also a generic lambda) is an object; a template function isn't an object but a set of objects. You can pass an object to a function, not a set of objects.



    But the preceding solution should works also for classes/structs with operator()s: when there is a single, non template, operator(), you should get 1 from foo(); otherwise (no operator(), more than one operator(), template operator()), foo() should return 0.






    share|improve this answer
















    Is there a way to detect this and give nice static_assert about it?




    I suppose you can use SFINAE over operator() type.



    Follows an example



    #include <type_traits>

    template <typename T>
    constexpr auto foo (T const &)
    -> decltype( &T::operator(), bool{} )
    { return true; }

    constexpr bool foo (...)
    { return false; }

    int main()
    {
    auto l1 = (int){ return 0; };
    auto l2 = (auto){ return 0; };

    static_assert( foo(l1), "!" );
    static_assert( ! foo(l2), "!" );
    }


    Instead of a bool, you can return std::true_type (from foo() first version) or std::false_type (from second version) if you want to use it through decltype().




    Would also be nice if it worked with function templates as well, not just with lambdas.




    I don't think it's possible in a so simple way: a lambda (also a generic lambda) is an object; a template function isn't an object but a set of objects. You can pass an object to a function, not a set of objects.



    But the preceding solution should works also for classes/structs with operator()s: when there is a single, non template, operator(), you should get 1 from foo(); otherwise (no operator(), more than one operator(), template operator()), foo() should return 0.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Apr 3 at 11:43

























    answered Apr 3 at 8:06









    max66max66

    39.1k74574




    39.1k74574








    • 2





      Trying to take the address of operator() was my initial idea as well (hence the comment) but then I somehow got lost in SFINAE on actually calling it. Anyway, here are some extra test cases involving overloaded functors: godbolt.org/z/M319jo

      – Max Langhof
      Apr 3 at 8:32
















    • 2





      Trying to take the address of operator() was my initial idea as well (hence the comment) but then I somehow got lost in SFINAE on actually calling it. Anyway, here are some extra test cases involving overloaded functors: godbolt.org/z/M319jo

      – Max Langhof
      Apr 3 at 8:32










    2




    2





    Trying to take the address of operator() was my initial idea as well (hence the comment) but then I somehow got lost in SFINAE on actually calling it. Anyway, here are some extra test cases involving overloaded functors: godbolt.org/z/M319jo

    – Max Langhof
    Apr 3 at 8:32







    Trying to take the address of operator() was my initial idea as well (hence the comment) but then I somehow got lost in SFINAE on actually calling it. Anyway, here are some extra test cases involving overloaded functors: godbolt.org/z/M319jo

    – Max Langhof
    Apr 3 at 8:32















    4














    Yet another simpler option:



    #include <type_traits>
    ...
    template <typename V>
    void Visit(V v) {
    class Auto {};
    static_assert(!std::is_invocable<V, Auto&>::value);
    static_assert(!std::is_invocable<V, Auto*>::value);
    ...
    }


    The Auto class is just an invented type impossible to occur in the V parameters. If V accepts Auto as an argument it must be a generic.



    I tested in coliru and I can confirm the solution covers these cases:



    Visit((auto x){}); // nice static assert
    Visit((auto *x){}); // nice static assert
    Visit((auto &x){}); // nice static assert
    Visit((auto &&x){}); // nice static assert


    I'm not sure if that would cover all the possible lambdas that you don't know which are :)






    share|improve this answer


























    • Nice idea ! You may ensure that "Auto" is unique like that : auto a_lambda = (){}; using Auto= decltype(a_lambda);

      – Martin m
      Apr 3 at 9:58













    • @Martinm This seems to be the idiomatic way but does it actually have an advantage over the code in this answer?

      – Konrad Rudolph
      Apr 3 at 10:07











    • Well, I am not sure what happens in the case where an "Auto" class already define somewhere else. In meta-programming context I prefer to be sure that my type cannot be in conflict in some obscure case.

      – Martin m
      Apr 3 at 10:19






    • 4





      This gives both false positives (e.g. Visit((std::any){});) and false negatives (Visit((int, auto){}); or Visit((auto*){});)

      – Barry
      Apr 3 at 11:47













    • Visit((int, auto){}) is not a valid case because of v(std::get<Arg1>(data)); and Visit((auto*){}); is fixed now. I'm not sure if Visit((std::any){}) is one of the cases to be avoided as the question isn't clear enough.

      – olivecoder
      Apr 3 at 12:09


















    4














    Yet another simpler option:



    #include <type_traits>
    ...
    template <typename V>
    void Visit(V v) {
    class Auto {};
    static_assert(!std::is_invocable<V, Auto&>::value);
    static_assert(!std::is_invocable<V, Auto*>::value);
    ...
    }


    The Auto class is just an invented type impossible to occur in the V parameters. If V accepts Auto as an argument it must be a generic.



    I tested in coliru and I can confirm the solution covers these cases:



    Visit((auto x){}); // nice static assert
    Visit((auto *x){}); // nice static assert
    Visit((auto &x){}); // nice static assert
    Visit((auto &&x){}); // nice static assert


    I'm not sure if that would cover all the possible lambdas that you don't know which are :)






    share|improve this answer


























    • Nice idea ! You may ensure that "Auto" is unique like that : auto a_lambda = (){}; using Auto= decltype(a_lambda);

      – Martin m
      Apr 3 at 9:58













    • @Martinm This seems to be the idiomatic way but does it actually have an advantage over the code in this answer?

      – Konrad Rudolph
      Apr 3 at 10:07











    • Well, I am not sure what happens in the case where an "Auto" class already define somewhere else. In meta-programming context I prefer to be sure that my type cannot be in conflict in some obscure case.

      – Martin m
      Apr 3 at 10:19






    • 4





      This gives both false positives (e.g. Visit((std::any){});) and false negatives (Visit((int, auto){}); or Visit((auto*){});)

      – Barry
      Apr 3 at 11:47













    • Visit((int, auto){}) is not a valid case because of v(std::get<Arg1>(data)); and Visit((auto*){}); is fixed now. I'm not sure if Visit((std::any){}) is one of the cases to be avoided as the question isn't clear enough.

      – olivecoder
      Apr 3 at 12:09
















    4












    4








    4







    Yet another simpler option:



    #include <type_traits>
    ...
    template <typename V>
    void Visit(V v) {
    class Auto {};
    static_assert(!std::is_invocable<V, Auto&>::value);
    static_assert(!std::is_invocable<V, Auto*>::value);
    ...
    }


    The Auto class is just an invented type impossible to occur in the V parameters. If V accepts Auto as an argument it must be a generic.



    I tested in coliru and I can confirm the solution covers these cases:



    Visit((auto x){}); // nice static assert
    Visit((auto *x){}); // nice static assert
    Visit((auto &x){}); // nice static assert
    Visit((auto &&x){}); // nice static assert


    I'm not sure if that would cover all the possible lambdas that you don't know which are :)






    share|improve this answer















    Yet another simpler option:



    #include <type_traits>
    ...
    template <typename V>
    void Visit(V v) {
    class Auto {};
    static_assert(!std::is_invocable<V, Auto&>::value);
    static_assert(!std::is_invocable<V, Auto*>::value);
    ...
    }


    The Auto class is just an invented type impossible to occur in the V parameters. If V accepts Auto as an argument it must be a generic.



    I tested in coliru and I can confirm the solution covers these cases:



    Visit((auto x){}); // nice static assert
    Visit((auto *x){}); // nice static assert
    Visit((auto &x){}); // nice static assert
    Visit((auto &&x){}); // nice static assert


    I'm not sure if that would cover all the possible lambdas that you don't know which are :)







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Apr 3 at 14:05

























    answered Apr 3 at 8:56









    olivecoderolivecoder

    2,0881217




    2,0881217













    • Nice idea ! You may ensure that "Auto" is unique like that : auto a_lambda = (){}; using Auto= decltype(a_lambda);

      – Martin m
      Apr 3 at 9:58













    • @Martinm This seems to be the idiomatic way but does it actually have an advantage over the code in this answer?

      – Konrad Rudolph
      Apr 3 at 10:07











    • Well, I am not sure what happens in the case where an "Auto" class already define somewhere else. In meta-programming context I prefer to be sure that my type cannot be in conflict in some obscure case.

      – Martin m
      Apr 3 at 10:19






    • 4





      This gives both false positives (e.g. Visit((std::any){});) and false negatives (Visit((int, auto){}); or Visit((auto*){});)

      – Barry
      Apr 3 at 11:47













    • Visit((int, auto){}) is not a valid case because of v(std::get<Arg1>(data)); and Visit((auto*){}); is fixed now. I'm not sure if Visit((std::any){}) is one of the cases to be avoided as the question isn't clear enough.

      – olivecoder
      Apr 3 at 12:09





















    • Nice idea ! You may ensure that "Auto" is unique like that : auto a_lambda = (){}; using Auto= decltype(a_lambda);

      – Martin m
      Apr 3 at 9:58













    • @Martinm This seems to be the idiomatic way but does it actually have an advantage over the code in this answer?

      – Konrad Rudolph
      Apr 3 at 10:07











    • Well, I am not sure what happens in the case where an "Auto" class already define somewhere else. In meta-programming context I prefer to be sure that my type cannot be in conflict in some obscure case.

      – Martin m
      Apr 3 at 10:19






    • 4





      This gives both false positives (e.g. Visit((std::any){});) and false negatives (Visit((int, auto){}); or Visit((auto*){});)

      – Barry
      Apr 3 at 11:47













    • Visit((int, auto){}) is not a valid case because of v(std::get<Arg1>(data)); and Visit((auto*){}); is fixed now. I'm not sure if Visit((std::any){}) is one of the cases to be avoided as the question isn't clear enough.

      – olivecoder
      Apr 3 at 12:09



















    Nice idea ! You may ensure that "Auto" is unique like that : auto a_lambda = (){}; using Auto= decltype(a_lambda);

    – Martin m
    Apr 3 at 9:58







    Nice idea ! You may ensure that "Auto" is unique like that : auto a_lambda = (){}; using Auto= decltype(a_lambda);

    – Martin m
    Apr 3 at 9:58















    @Martinm This seems to be the idiomatic way but does it actually have an advantage over the code in this answer?

    – Konrad Rudolph
    Apr 3 at 10:07





    @Martinm This seems to be the idiomatic way but does it actually have an advantage over the code in this answer?

    – Konrad Rudolph
    Apr 3 at 10:07













    Well, I am not sure what happens in the case where an "Auto" class already define somewhere else. In meta-programming context I prefer to be sure that my type cannot be in conflict in some obscure case.

    – Martin m
    Apr 3 at 10:19





    Well, I am not sure what happens in the case where an "Auto" class already define somewhere else. In meta-programming context I prefer to be sure that my type cannot be in conflict in some obscure case.

    – Martin m
    Apr 3 at 10:19




    4




    4





    This gives both false positives (e.g. Visit((std::any){});) and false negatives (Visit((int, auto){}); or Visit((auto*){});)

    – Barry
    Apr 3 at 11:47







    This gives both false positives (e.g. Visit((std::any){});) and false negatives (Visit((int, auto){}); or Visit((auto*){});)

    – Barry
    Apr 3 at 11:47















    Visit((int, auto){}) is not a valid case because of v(std::get<Arg1>(data)); and Visit((auto*){}); is fixed now. I'm not sure if Visit((std::any){}) is one of the cases to be avoided as the question isn't clear enough.

    – olivecoder
    Apr 3 at 12:09







    Visit((int, auto){}) is not a valid case because of v(std::get<Arg1>(data)); and Visit((auto*){}); is fixed now. I'm not sure if Visit((std::any){}) is one of the cases to be avoided as the question isn't clear enough.

    – olivecoder
    Apr 3 at 12:09













    3














    #include <variant>
    #include <string>
    #include <iostream>

    template <class U, typename T = void>
    struct can_be_checked : public std::false_type {};

    template <typename U>
    struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};

    template <typename U>
    struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};


    template<typename Ret, typename Arg, typename... Rest>
    Arg first_argument_helper(Ret(*) (Arg, Rest...));

    template<typename Ret, typename F, typename Arg, typename... Rest>
    Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

    template<typename Ret, typename F, typename Arg, typename... Rest>
    Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

    template <typename F>
    decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

    template <typename T>
    using first_argument = decltype(first_argument_helper(std::declval<T>()));

    std::variant<int, std::string> data="abc";


    template <typename V>
    void Visit(V v){
    if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
    {
    using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
    if (! std::holds_alternative<Arg1>(data))
    {
    std::cerr<< "alternative mismatchn";
    return;
    }
    v(std::get<Arg1>(data));
    }
    else
    {
    std::cout << "it's a template / auto lambda " << std::endl;
    }


    }

    template <class T>
    void foo(const T& t)
    {
    std::cout <<t << " foo n";
    }

    void fooi(const int& t)
    {
    std::cout <<t << " fooi " << std::endl;
    }

    int main(){
    Visit((const int& i){std::cout << i << std::endl; });
    Visit((const std::string& s){std::cout << s << std::endl; });
    Visit((auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
    Visit(foo<int>);

    Visit<decltype(fooi)>(fooi);
    Visit(fooi);


    // Visit(foo); // => fail ugly
    }


    I don't know if it's you want, but you can, with that static_assert if an auto lambda is passed as parameter.



    I think it's not possible to do the same for template function, but not sure.






    share|improve this answer




























      3














      #include <variant>
      #include <string>
      #include <iostream>

      template <class U, typename T = void>
      struct can_be_checked : public std::false_type {};

      template <typename U>
      struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};

      template <typename U>
      struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};


      template<typename Ret, typename Arg, typename... Rest>
      Arg first_argument_helper(Ret(*) (Arg, Rest...));

      template<typename Ret, typename F, typename Arg, typename... Rest>
      Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

      template<typename Ret, typename F, typename Arg, typename... Rest>
      Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

      template <typename F>
      decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

      template <typename T>
      using first_argument = decltype(first_argument_helper(std::declval<T>()));

      std::variant<int, std::string> data="abc";


      template <typename V>
      void Visit(V v){
      if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
      {
      using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
      if (! std::holds_alternative<Arg1>(data))
      {
      std::cerr<< "alternative mismatchn";
      return;
      }
      v(std::get<Arg1>(data));
      }
      else
      {
      std::cout << "it's a template / auto lambda " << std::endl;
      }


      }

      template <class T>
      void foo(const T& t)
      {
      std::cout <<t << " foo n";
      }

      void fooi(const int& t)
      {
      std::cout <<t << " fooi " << std::endl;
      }

      int main(){
      Visit((const int& i){std::cout << i << std::endl; });
      Visit((const std::string& s){std::cout << s << std::endl; });
      Visit((auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
      Visit(foo<int>);

      Visit<decltype(fooi)>(fooi);
      Visit(fooi);


      // Visit(foo); // => fail ugly
      }


      I don't know if it's you want, but you can, with that static_assert if an auto lambda is passed as parameter.



      I think it's not possible to do the same for template function, but not sure.






      share|improve this answer


























        3












        3








        3







        #include <variant>
        #include <string>
        #include <iostream>

        template <class U, typename T = void>
        struct can_be_checked : public std::false_type {};

        template <typename U>
        struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};

        template <typename U>
        struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};


        template<typename Ret, typename Arg, typename... Rest>
        Arg first_argument_helper(Ret(*) (Arg, Rest...));

        template<typename Ret, typename F, typename Arg, typename... Rest>
        Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

        template<typename Ret, typename F, typename Arg, typename... Rest>
        Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

        template <typename F>
        decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

        template <typename T>
        using first_argument = decltype(first_argument_helper(std::declval<T>()));

        std::variant<int, std::string> data="abc";


        template <typename V>
        void Visit(V v){
        if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
        {
        using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
        if (! std::holds_alternative<Arg1>(data))
        {
        std::cerr<< "alternative mismatchn";
        return;
        }
        v(std::get<Arg1>(data));
        }
        else
        {
        std::cout << "it's a template / auto lambda " << std::endl;
        }


        }

        template <class T>
        void foo(const T& t)
        {
        std::cout <<t << " foo n";
        }

        void fooi(const int& t)
        {
        std::cout <<t << " fooi " << std::endl;
        }

        int main(){
        Visit((const int& i){std::cout << i << std::endl; });
        Visit((const std::string& s){std::cout << s << std::endl; });
        Visit((auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
        Visit(foo<int>);

        Visit<decltype(fooi)>(fooi);
        Visit(fooi);


        // Visit(foo); // => fail ugly
        }


        I don't know if it's you want, but you can, with that static_assert if an auto lambda is passed as parameter.



        I think it's not possible to do the same for template function, but not sure.






        share|improve this answer













        #include <variant>
        #include <string>
        #include <iostream>

        template <class U, typename T = void>
        struct can_be_checked : public std::false_type {};

        template <typename U>
        struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};

        template <typename U>
        struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};


        template<typename Ret, typename Arg, typename... Rest>
        Arg first_argument_helper(Ret(*) (Arg, Rest...));

        template<typename Ret, typename F, typename Arg, typename... Rest>
        Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

        template<typename Ret, typename F, typename Arg, typename... Rest>
        Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

        template <typename F>
        decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

        template <typename T>
        using first_argument = decltype(first_argument_helper(std::declval<T>()));

        std::variant<int, std::string> data="abc";


        template <typename V>
        void Visit(V v){
        if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
        {
        using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
        if (! std::holds_alternative<Arg1>(data))
        {
        std::cerr<< "alternative mismatchn";
        return;
        }
        v(std::get<Arg1>(data));
        }
        else
        {
        std::cout << "it's a template / auto lambda " << std::endl;
        }


        }

        template <class T>
        void foo(const T& t)
        {
        std::cout <<t << " foo n";
        }

        void fooi(const int& t)
        {
        std::cout <<t << " fooi " << std::endl;
        }

        int main(){
        Visit((const int& i){std::cout << i << std::endl; });
        Visit((const std::string& s){std::cout << s << std::endl; });
        Visit((auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
        Visit(foo<int>);

        Visit<decltype(fooi)>(fooi);
        Visit(fooi);


        // Visit(foo); // => fail ugly
        }


        I don't know if it's you want, but you can, with that static_assert if an auto lambda is passed as parameter.



        I think it's not possible to do the same for template function, but not sure.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Apr 3 at 8:12









        Martin mMartin m

        885




        885






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55488333%2fis-it-possible-to-static-assert-that-a-lambda-is-not-generic%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Plaza Victoria

            Puebla de Zaragoza

            Musa