How Can I Write a C++ Template Which is Inferred From a Function Argument?

Viewed 87

I'm trying to write a C++ function which generates a vector from two calls to an argument function. The argument function accepts an integer pointer and a pointer to an array of elements. If called with nullptr for elements, it will fill the integer with how many elements it has to produce. Then on the second call, it will populate the element memory with that many instances.

So, this pattern can be used to get a vector of elements. I'm repeating this code a lot so I wrote a function which does this for me:

template<typename TElement>
std::vector<TElement> LoadArray(std::function<void(uint32_t*, TElement*)> const func) {
  uint32_t count;
  func(&count, nullptr);

  std::vector<TElement> elements(count);
  func(&count, elements.data());

  return elements;
}

However, when calling this function, I have to specify TElement in the template as well as the function:

std::vector<Cat> cats = LoadArray<Cat>([](uint32_t* const count, Cat* const cats) {
  LoadCats(count, cats);
});

How can I make this type inference automatic in C++20?

Edit: All of the functions have different signatures. Some have return types, others don't; some take an argument before the count and array, some take arguments after.

1 Answers

A simple-ish way to do this is to delay the lookup until you are within the function by having it return auto.

Something along these general lines:

// The stolen function_traits struct...thing
template<typename T>
struct load_array_cb_traits;

template<typename Ret, typename Arg2>
struct load_array_cb_traits<std::function<Ret(uint32_t*, Arg2*)>> {
  using return_type = Ret;
  using elem_type = Arg2;
};

template<typename FuncT>
auto LoadArray(const FuncT& func) {
  using traits = load_array_cb_traits<decltype(std::function{func})>;

  using TElement = std::decay_t<typename traits::elem_type>;
  using TRet = std::decay_t<typename traits::return_type>;

  uint32_t count;
  func(&count, nullptr);

  std::vector<TElement> elements(count);
  func(&count, elements.data());

  return elements;
}
Related