3

I'm writing a program for numerical simulations. One of my functions takes callbacks. The function is implemented as a template function and callbacks as template classes, so that calls to the callbacks may be inlined.

Now, my function is getting complicated, and I'd like to declare the signatures of callbacks in a well-defined, auto-checkable way. So I looked to C++ Concepts TS.

And here is the problem. I'd like to define a concept of a functor that takes anstd::array, for example

struct sum {
    template <class Float, size_t N>
    Float operator()(std::array<Float, N> const& arr) {
       // return the sum of arr...
    }
};
struct avg {
    template <class Float, size_t N>
    Float operator()(std::array<Float, N> const& arr) {
        // return the average of arr...
    }
};

Note: This is a simplified example. Changing the signature of functors (e.g. std::array to a pair of iterators) is not impossible but not preferred.

How to write a concept that represents the common interface of sum and avg above?

First I tried:

template <class F, class Float, size_t N>
concept bool ArrayProcessor = requires (F func, std::array<Float, N> arr) {
    func(arr);
};

But this lead to (in my opinion) an ugly code; at the definition of the template function that uses the callbacks, its declaration looks like this:

template <ArrayProcessor<double, 1> CB, ...>
void simulate(CB const& callback, ...) {
    ...
}

The <double, 1> part is a dummy required for the compilation to pass. This solution is hardly acceptable because the template argument for callback.operator() may change inside simulate; different types and sizes of std::arrays may be passed to it.

Second I tried:

template <class F>
concept bool ArrayProcessor = requires (F func, auto arr) {
    func(arr);
};

This code is ill-formed because it's ill-formed to use constrained-type-specifier in requires-expression (reference: https://groups.google.com/a/isocpp.org/forum/m/#!topic/concepts/_gHn0wJJNGA). By the way gcc 6.1.0 produces an internal compilation error against it.

So, the question is: How can I define a concept for functors that takes template classes as their parameters (preferrably without workarounds)?

Through my searching, I got the impression that C++ Concepts may not be designed for uses like this. Suggestions to alternatives to C++ Concepts are greatly appreciated as well.

(Maybe related to Specifying a concept for a type that has a member function template using Concepts Lite but I can't tell if it's duplicate)

Community
  • 1
  • 1
Ohashi
  • 397
  • 3
  • 14
  • 1
    IMO, your link is a duplicate. – Jarod42 Aug 06 '16 at 10:24
  • @Jarod42 I saw your deleted comment, and tried `is_std_array` and yes, I'm stuck; without introducing another template parameter to the concept, it seems I can't incorporate the trait into it. I'd like this question kept open because the underlying problem with C++ language and Concepts is the same, but a workaround for _my_ problem (auto-checkable callback signature declaration/documentation) may exist. – Ohashi Aug 06 '16 at 11:02
  • Have you profiled using `std::function` and determined the overhead is significant? Because unless the arrays are small, the result is discarded, the operation is empty, or you expect the compiler to unroll multiple array procrssing and pipeline them, the std function call overhead is going to be tiny. – Yakk - Adam Nevraumont Aug 06 '16 at 12:31
  • The same concern I placed on the duplicate applies. Suppose you have a function object that works for everything except arrays of size 7, where sfinae occurs. Does it pass your concept? Asking "does this work for every array" requires the compiler solve Halt, which seems unreasonable. If "any array", well you wrote that. And the code below will only actually pass in *some* array sizes, you could just test exactly those. – Yakk - Adam Nevraumont Aug 06 '16 at 12:42
  • @Yakk (To the first comment) No, I haven't, and I don't think its overhead will matter a lot, either. The functor typically takes an array of 24 `double`s and returns a (maybe) modified array with the same size. The functor is either over-hundred-lines function or no-op. What I'm wondering is that introducing runtime overhead, however small it is, solely for compile-time signature check is worth it. I'm fine to resort to well-documented comments, considering that I'll be the only user of the code. – Ohashi Aug 06 '16 at 12:51
  • @Yakk (To the second) I think I understand the duplicate. Compilers cannot check every argument the concept may take; they have to check concept-compliance without substituting template parameters and they can't because of e.g. template specializations. I'm glad to accept such inconveniences; they have good reasons. I'd rather look for other way (in the worst case well-documented comments) than sticking to Concepts and fixing the template argument of array (in my usecase, to `std::array`). – Ohashi Aug 06 '16 at 13:02
  • The runtime overhead isn't for purely compile-time checks. It lets you split the implementation out of header files, speeds of build times, generates errors earlier and locally, and overall simplifies maintenance. The point is that you should only use header-file only code when (A) it must be done that way (type erasure etc), (B) you know it can cause performance costs, or (C) you are writing low-level stuff that could be used in (B), or (D) you are playing around. Writing *simpler* code means you have more time to profile *and optimize the parts that actually matter*, after you check. – Yakk - Adam Nevraumont Aug 06 '16 at 13:36

0 Answers0