By the way, it’s probably also worth pointing out what you gain from the way Julia is set up, since I agree it’s awkward to enforce this kind of contract.
In C or C++, the most obvious solution to your problem would be to take a function pointer or a std::function
with the appropriate signature. You’d then just call that function pointer within the loop, and your problem would be solved, right?
Maybe not: It turns out that can actually be a performance trap. If you take your input bar
as a function pointer, then every time you call that function you are actually performing a function call, which has a measurable overhead even in C. This means that if your bar
is some very cheap operation, then you’ll get much worse performance by passing it as a function pointer than if you’d just hard-coded that function in place. That’s because an opaque function pointer prevents the compiler from inlining that function call, which would eliminate that overhead otherwise.
For better performance in C++, we’d instead generally do something like:
template <typename F>
void foo(int c, const F& bar) {
};
and pass a C++ lambda or other C++ function directly as bar
, without casting it to a function pointer at all. By making the exact type of bar
visible to the compiler, we allow the compiler to inline bar
if it chooses to, resulting in better performance. But now we’ve lost the ability to easily constrain the signature of bar
(it’s not even necessarily a function anymore, just some thing we can call with ()). So then we start adding some unholy template metaprogramming std::result_of
stuff, which nobody actually enjoys. In other words, our effort to write high-performance C++ has resulted in something that looks a lot more like Julia.
In Julia, you can think of every function as operating like a template function in C++. In particular, since every function in Julia has a distinct type, the compiler can generate specialized code for foo
for each different bar
you provide, potentially inlining that bar
call if necessary.
If you want to reproduce the C-style function pointer approach (and associated slowness), you can do exactly that with GitHub - yuyichao/FunctionWrappers.jl , but I wouldn’t recommend it.