0

I would like to let compiler deduce partially class template arguments from constructor.

The motivation is to write a protocol library where the existance (here the length in bits) of certain data depends of the value of last variable, so a conditional class must be used to model this.

The c++ code I want to implement should work like this, but I would like to implement it in a more expressive and simplified way, without having to set all the parameters in the template but leaving compiler deduce them:

Coliru link: https://coliru.stacked-crooked.com/a/bb15abb2a9c09bb1

#include <iostream>

template<typename T1, typename T2, typename F, int... Ints>
struct If : T1
{
    const T2& condition;
    constexpr If(const T2& cond) : condition(cond) {}

    constexpr int bits() { return check() ? T1::bits : 0; }

    constexpr bool check()
    {
        return (F{}(Ints, condition.value) || ...);
    }
};

struct Variable1
{
    int value{};
    static constexpr int bits{ 5 };
};

struct Variable2
{
    int value{};
    static constexpr int bits{ 8 };
};

struct Datagram
{
    Variable1 var1;
    If<Variable2, Variable1, std::equal_to<int>, 1, 2> var2{ var1 };//It compiles and works OK under c++17. What I have...
    //If<Variable2> var2{ var1, std::equal_to<int>{}, 1, 2 };// ...what I wish
};

int main()
{
    Datagram data;
    data.var1.value = 0;
    std::cout << data.var2.bits() << "\n";//must be 0
    
    data.var1.value = 1;
    std::cout << data.var2.bits() << "\n";//must be 8
    
    data.var1.value = 2;
    std::cout << data.var2.bits() << "\n";//must be 8
}

Is this possible?

Pablo
  • 557
  • 3
  • 16
  • 1
    Does this answer your question? [Why not infer template parameter from constructor?](https://stackoverflow.com/questions/984394/why-not-infer-template-parameter-from-constructor). It is possible using C++17 and [Class template argument deduction (CTAD)](https://en.cppreference.com/w/cpp/language/class_template_argument_deduction) – dtell Jan 24 '22 at 11:56
  • @dtell Thank you, it's kind of an explanation of what I want it, but I don't really know how to program so it works. I am going to edit the question with a more concisse example and a coliru link in case you wanna give it a try and provide a formal answer to my question – Pablo Jan 24 '22 at 17:32
  • 3
    I didn't look too closely at the code, but you're not going to be able to deduce the `int` parameters from constructor arguments. And CTAD only works when you specify no template arguments at all. – HolyBlackCat Jan 24 '22 at 17:38
  • @HolyBlackCat Thank you. Yeah, as you mentioned int paramenter were my worst aware about CTAD. By the other hand, do you think is would be possible at least to deduce the argument of the variable given as argument in constructor? And if so, any advise of how to do it? – Pablo Jan 24 '22 at 17:45
  • @Pablo: You should edit your question to remove the plainly impossible parts (deducing from constructor parameters that don’t even exist) so it can be answered. – Davis Herring Jan 25 '22 at 14:38
  • @Davis Herring Sorry, can you please be more concise? I would like to do so but I don't really know what parts are those. If you feel in the mood, you can provide a formal answer where you explain the drawbacks in the question and the correct approach to solve the problem as much as possible. – Pablo Jan 25 '22 at 15:00
  • @Pablo: What needs to be fixed to make the question answerable as stated is that your `If var2{ var1, std::equal_to{}, 1, 2 };` declaration tries to pass four arguments to a set of constructors that all have 1 parameter. That can't work regardless of what deduction possibilities there are. – Davis Herring Jan 25 '22 at 18:47
  • @Davis Herring Ah, ok, now I get it. Yes, you are right. This is the way I have got this working with a template class and a non-template constructor. The idea was transforming it into a template constructor, but I rather prefered to leave here a compilable and working solution instead of a non-compilable one. Please, feel free to change whatever you want as user Igor Tandetnik did in his well documented response, if you think you have a better solution. Thanks – Pablo Jan 25 '22 at 18:52
  • @Pablo: I think I see now: the commented-out declaration uses a constructor you wish you could write (as a non-template that would support the partial CTAD that doesn’t exist). – Davis Herring Jan 25 '22 at 18:56

1 Answers1

2

The concept you are probably looking for is "type erasure", e.g. via std::function. Something along these lines:

template<typename T1>
struct If : T1
{
    std::function<bool()> checker_;

    template <typename T2, typename F, typename... Args>
    constexpr If(const T2& cond, F&& f, Args&&... args)
      : checker_([=, &cond]() { return (f(args, cond.value) || ...); })
    {}


    constexpr int bits() { return check() ? T1::bits : 0; }

    constexpr bool check()
    {
        return checker_();
    }
};

Demo

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Thank you, it is a very nice apport. I would not even have thought in this kind of solution by myself. The only con (tiny one) I can see is the small overcharge that ```std::function``` introduces. – Pablo Jan 25 '22 at 15:53