6

EDIT: This question was originally titled "Using std::bind to create inline function," but that's not really what I'm after: I just want a simple way to alias functions.

I would like to expose std::chrono::high_resolution_clock::now as a standalone function. That is, I would like to do the following:

auto current_time = std::bind(std::chrono::high_resolution_clock::now);

Unfortunately, since this is in a header file, it results in multiple definitions of current_time at link-time. Is there a way to return an inline function from std::bind?

Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • 3
    What's wrong with `inline auto current_time() { return std::chrono::high_resolution_clock::now(); }`? – T.C. Mar 06 '15 at 23:38
  • 1
    @T.C. Nothing in particular (it's what I'm using as a workaround), I just happen to think it's somewhat ugly. What I'd *really* like is function-aliasing, the way we have type-aliasing; i.e., `using current_time = std::chrono::high_resolution_clock::now;` would essentially behave like `#define current_time std::chrono::high_resolution_clock::now` but without preprocessor involvement. – Kyle Strand Mar 06 '15 at 23:53
  • For the header you can always give it internal linkage. – T.C. Mar 07 '15 at 00:51
  • @T.C. I may be misunderstanding you, but the point is to *expose* this function in multiple translation units--hence the need for `inline`. – Kyle Strand Mar 07 '15 at 00:57
  • Why does it need to be inline to use it in multiple translation units? Declare the function in the header, define it non-inline as a call to `std::chrono::high_resolution_clock::now()`. Not sure why I'm even commenting on this ... just keeping using the function you already have (it's not a workaround, it's the solution). – Jonathan Wakely Mar 07 '15 at 01:22
  • @JonathanWakely It causes "multiple-definition" link errors if not inline. – Kyle Strand Mar 07 '15 at 01:28
  • Not if you declare a function and define it non-inline in one translation unit. Obviously if you have a function in a header and don't declare it inline, yes, you get multiple definitions ... so don't do that. – Jonathan Wakely Mar 07 '15 at 01:32
  • @JonathanWakely As pointed out in my comment to T.C., the whole point is to expose this as a utility function, which is why it's in a header in the first place. – Kyle Strand Mar 07 '15 at 01:34
  • That doesn't follow. You can **declare** a utility function in a header. That doesn't mean you have to **define** it in a header. Unless you have some peculiar definition of "utility function". – Jonathan Wakely Mar 07 '15 at 01:35
  • @JonathanWakely I would like the entire thing to be in the header, for two reasons: (1) it's just an alias, and this whole utility header is so simple that I have no reason to write a corresponding `cpp`. (2) The return type should be `auto`, because otherwise it's a horrible nightmarish line of text, and `auto` return types require either a `decltype` or a full definition. – Kyle Strand Mar 07 '15 at 01:40
  • OK, those are reasons to make it an inline function. T.C.'s first comment is still the best option, it is easier to understand and probably performs better than any of the answers. – Jonathan Wakely Mar 07 '15 at 01:42

5 Answers5

8

Here's what I do if I want to create a simple function alias

constexpr auto &&now = std::chrono::high_resolution_clock::now;

and if I want to create a full wrapper alias that will be inlined

template<typename ... Args>
inline constexpr auto now(Args &&... args) -> decltype(std::chrono::high_resolution_clock::now(std::forward<Args>(args)...)){
    return std::chrono::high_resolution_clock::now(std::forward<Args>(args)...);
}

The reason why I use a universal reference auto&& in the alias definition is because of the possibility of addressof(now) == addressof(std::chrono::high_resolution_clock::now).

On my system with G++ 4.9.2 running this:

constexpr auto &&now_ref = std::chrono::high_resolution_clock::now;
constexpr auto now_var = std::chrono::high_resolution_clock::now;

template<typename ... Args>
inline constexpr auto now_wrapper(Args &&... args)
    -> decltype(std::chrono::high_resolution_clock::now(std::forward<Args>(args)...)){
    return std::chrono::high_resolution_clock::now(std::forward<Args>(args)...);
}

int main(int argc, char *argv[]){
    std::cout << std::hex << std::showbase;
    std::cout << (uintptr_t)std::addressof(std::chrono::high_resolution_clock::now) << '\n';
    std::cout << (uintptr_t)std::addressof(now_wrapper<>) << '\n';
    std::cout << (uintptr_t)std::addressof(now_var) << '\n';
    std::cout << (uintptr_t)std::addressof(now_ref) << '\n';
}

I get the following results:

0x4007c0
0x400a50
0x400ae8
0x4007c0

Showing that only the auto&& is actually a direct alias of the function, whereas all other methods have some level of indirection. (although, after compilation they may be replaced by inlined function calls. maybe.)

pablo285
  • 2,460
  • 4
  • 14
  • 38
RamblingMad
  • 5,332
  • 2
  • 24
  • 48
  • 1
    That *may* be inlined. – Neil Kirk Mar 07 '15 at 01:37
  • 1
    Interesting. Could you add a bit of explanation of why you use `static` and `&&`, since `constexpr auto` is sufficient to get this to work? – Kyle Strand Mar 07 '15 at 01:37
  • The `static` definition is so I don't run into multiple definition errors when defined in a header (a problem you ran into). The `auto&&` syntax is actually different to a regular type with `&&` after it, `auto&&` is actually somethin called a 'universal reference'. Ill leave that up to you to read about. If you use `constexpr auto` by itself you will prob'ly get a function pointer, but if you use `constexpr auto&&` you get a reference of the function such that `addressof(now) == addressof(chrono::high_resolution_clock::now)` as opposed to `now == addressof(chrono::high_resolution_clock::now)` – RamblingMad Mar 07 '15 at 01:49
  • @NeilKirk if you really need it to be, you could add `[[always_inline]]` to the function definition (or the non-gcc equivalent) – RamblingMad Mar 07 '15 at 01:50
  • @KyleStrand and because compilers are smart, the reference will be replaced by actual function calls in best-case-scenario (good optimization and such) – RamblingMad Mar 07 '15 at 01:52
  • Good compilers can inline the first one, so the wrapper is overkill and doesn't add any extra guarantee of inlining. – Neil Kirk Mar 07 '15 at 01:55
  • @NeilKirk but that will only work in this case (non-templated, non-return-type-deduced, non-overloaded, simple function) – RamblingMad Mar 07 '15 at 01:57
  • It's a good job the function is non-templated, non-return-type-decuded, non-overloaded and simple! – Neil Kirk Mar 07 '15 at 02:01
  • 1
    @NeilKirk otherwise I wouldn't have re-suggested the first idea ;) but it's good for OP to learn the second way for more special cases. – RamblingMad Mar 07 '15 at 02:03
  • @CoffeeandCode `constexpr` implies inlining, so `static` is unnecessary. – Kyle Strand Mar 07 '15 at 02:30
  • Other than that, though, this is a really nice answer, though you should edit it to include the details you put in your comments. – Kyle Strand Mar 07 '15 at 02:32
3

I don't think there is anyway to do this as bind is not constexpr.

Also lambdas are not constexpr-able.

Edit: there is this trick to make a constexpr-like lambda http://pfultz2.com/blog/2014/09/02/static-lambda/

tahsmith
  • 1,643
  • 1
  • 17
  • 23
3

Adding another answer 'cause it takes a very different tack to what you want.

std::bind isn't necessary in this case, because no 'binding' is happening.

However I feel this could lead to some confusing problems down the line, since current_time isn't really an alias in the same way that using delcarations are.

#include <iostream>
#include <chrono>

using namespace std;

auto constexpr current_time = std::chrono::high_resolution_clock::now;

int main() {
    auto now = current_time();
    cout << std::chrono::system_clock::to_time_t(now) << endl;
    return 0;
}
tahsmith
  • 1,643
  • 1
  • 17
  • 23
2

Using GCC it is possible to create a "function alias", but only for functions which are defined in the same translation unit and for which you know the mangled name, so it's not possible to do reliably for std::chrono::high_resolution_clock::now()

See the alias attribute at https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
2

Keep it simple.

const auto current_time = std::chrono::high_resolution_clock::now;
Neil Kirk
  • 21,327
  • 9
  • 53
  • 91