0

I want to use a fold expression but the function will only ever be used with one known parameter pack.

i.e.

template <class... Types>
Foo fn_impl(Arg arg) {
  // here, a fold expression involving Types... that returns a Foo
}

Foo fn(Arg arg) {
    return fn_impl<Bar, Baz>(arg);
}

And that's it, fn_impl won't ever be used again. Is there a way to make this less verbose? Ideally, I'd like to write the implementation of fn in its body, without a separate implementation function (which adds noise, imo).

I know I could "unroll" the fold expression with the types in the parameter pack by hand, but in this case, using a fold expression is just very convenient to make the implementation of fn not too verbose.

Here's a complete example, see highlighted QUESTION comment:

#include <cassert>
#include <cstdio>
#include <memory>

struct Base {
  virtual ~Base() {}
  virtual const char *get_name() const = 0;
};

template <class Derived> struct Base_CRTP : public Base {
  const char *get_name() const final {
    return static_cast<const Derived *>(this)->name;
  }
};

struct A : Base_CRTP<A> {
  static constexpr const char *name = "A";
};
struct B : Base_CRTP<B> {
  static constexpr const char *name = "B";
};

#define ITest_DERIVED_CLASSES A, B

// QUESTION: Can this be entirely moved into the definition of #1?
template <class IType, class... Types>
std::unique_ptr<IType> make_by_class_index___impl(int class_index) {
  int i = 0;
  std::unique_ptr<IType> ret;
  ([&] {
    if (i++ == class_index)
      ret = std::make_unique<Types>();
    return ret != nullptr;
  }() ||
   ...);
  return ret;
}

// #1
std::unique_ptr<Base> make_by_class_index(int class_index) {
  return make_by_class_index___impl<Base, ITest_DERIVED_CLASSES>(class_index);
}

template <class... Types> void print_pack_names() { (puts(Types::name), ...); }

int main() {

  print_pack_names<ITest_DERIVED_CLASSES>();

  puts("");

  auto p = make_by_class_index(0);
  assert(p != nullptr);
  printf("p name: %s\n", p->get_name());

  auto p2 = make_by_class_index(1);
  assert(p2 != nullptr);
  printf("p2 name: %s\n", p2->get_name());

  auto p3 = make_by_class_index(99);
  assert(p3 == nullptr);
}
dfrib
  • 70,367
  • 12
  • 127
  • 192
Nebular Noise
  • 388
  • 3
  • 15
  • 2
    Can you show how you are using/want to use `Types...` to create a `Foo`? – NathanOliver Dec 16 '22 at 14:17
  • What is the issue? Can you provide [mcve]? With current code sample I have no idea what is the problem: https://godbolt.org/z/nP5azEEhz I have no idea what are you trying to achieve, what suppose to be less verbose? – Marek R Dec 16 '22 at 14:34
  • 2
    You might use a lambda, but you need to give us more details. Is C++20 an option? – Bob__ Dec 16 '22 at 14:43

1 Answers1

1

In lack of sufficient details, let's assume without loss of generality that Arg is int that that Foo, Bar and Baz are defined as follows:

struct Foo { int x; };
struct Bar { static constexpr int foo() { return 1; } };
struct Baz { static constexpr int foo() { return 2; } };

If you may use C++20 you can migrate a) a variadic function which contains a fold expression and b) a call to site to it, e.g.:

template <typename... Types> Foo fn_impl(int arg) {
  return { arg + (Types::foo() + ...) };
}

// at call site
fn_impl<Bar, Baz>(arg);

into a single generic immediately invoked lambda, leveraging that P0428R2 (introduced in C++20) allows template heads for generic lambdas:

Foo fn(int arg) {
  return []<typename... Types>(int arg) -> Foo {
    return { arg + (Types::foo() + ...) };
  }
  .operator()<Bar, Baz>(arg);
}

This arguably looks quite complex, though, particularly as you need to use the operator name syntax to provide explicit template arguments for the generic lambdas. The separate function approach is arguably easier to follow for future maintainers.

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • Yes, exactly what I wanted to know! I did not consider using an immediately invoked template lambda, but it makes a lot of sense. Since I'm just tinkering at this point, I'm not too worried about maintainability. – Nebular Noise Dec 16 '22 at 15:11