8

I'm working with SDL which is a C library that has declarations of functions like this:

void SDL_foo(SDL_Rect *rect);

I have my own wrappers around some of the functions like this:

void foo(SDL_Rect rect) {
  SDL_foo(&rect);
}

This is so I can simply call them like this:

foo({x, y, w, h});

My question is: is it possible to avoid having a wrapper function and do something like this:

SDL_foo(&{x, y, w, h});

Thanks!

Kristopher Ives
  • 5,838
  • 7
  • 42
  • 67
  • reference can not bind to temporary variable – rsy56640 Jun 22 '18 at 04:09
  • Looks more like C than C++ – Sebastian D'Agostino Jun 22 '18 at 04:10
  • @sebadagostino SDL is a C library I am asking if there is a way to accomplish this "syntactic sugar" in C++ – Kristopher Ives Jun 22 '18 at 04:11
  • 2
    Feel free to answer with "No" and provide a reason – Kristopher Ives Jun 22 '18 at 04:14
  • Short answer: no. The initialiser `{x,y,w,h}` creates an object as a temporary, in order to pass it by value to the function. `&{x,y,w,h}` attempts to obtain a pointer to that temporary, which is not permitted. It works in your wrapper, because `rect` is a named variable, not a temporary (at least, in the scope of the function). – Peter Jun 22 '18 at 04:15
  • @Peter is there a macro of some kind that can automate this easily or am I stuck making wrapper functions? – Kristopher Ives Jun 22 '18 at 04:27
  • 2
    There are options. Writing a wrapper is the advisable option. Macros are actively discouraged in C++ for good reasons. Using a macro for this will encounter all sorts of factors that underpin general advice to not use them. – Peter Jun 22 '18 at 04:43
  • You can't get the address of an rvalue, because, unlike lvalues, they don't occupy identifiable locations in memory (lvalue stands for locator value). If that lib you're working with had an overhaul to allow rvalue references (a concept introduced by C++11), things would be easier, I suspect. – FinnTheHuman Jun 22 '18 at 06:14
  • 1
    You should not pass around `structs` or objects by value. Pass them by `const` reference. – user207421 Jun 22 '18 at 07:11
  • @EJP that's not good advice. OP's first example of materialising the temporary in the wrapper argument is exactly the sort of situation where it is appropriate – Caleth Jun 22 '18 at 08:14
  • @EJP: That's true for some structs, but not for others. It's certainly ok to pass, say, a `struct point { double x; double y; }` by value. But - my answer does agree with you in this particular case. – einpoklum Jun 22 '18 at 21:49

2 Answers2

8

No, you cannot do that because you can't get the address of a temporary.
But you can probably get away with it with a kind of wrapper like this:

struct MyRect {
    MyRect(SDL_rect rect): rect{rect} {}
    operator SDL_rect *() { return ▭ }
    SDL_rect rect;
};

SDL_foo(MyRect{{x, y, w, h}});

Not tested yet, but it should give you an idea of what I mean.
This way, instead of creating wrappers for all the functions from SDL, you have to create only a tiny wrapper around SDL_rect.
Not sure if it works fine for you anyway.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Not far, but you should just add some more ctors true copy/move constructors (current on take an object by copy) and most of all `MyRect(int x, int y, int w, int h): rect{x, y, w, h} {}` – Serge Ballesta Jun 22 '18 at 08:23
  • And if you do this, you probably want to template it so `SDL_foo(Local({x, y, w, h}))`. – CompuChip Jun 22 '18 at 21:53
  • @SergeBallesta Given that `SDL_rect` is a type from a C library, there's no way its own move operations could possibly have meanings different from the copy operations. I might even consider removing the existing `MyRect` constructor so that the type becomes an aggregate. – aschepler Jun 22 '18 at 21:57
  • @aschepler good point of removing the constructor of `MyRect` indeed. – skypjack Jun 23 '18 at 04:08
0

tl;dr: Just use references.

You can completely avoid your problem, and get the convenient calling convention you like - without instantiating any unnecessary copies of structs - if your wrappers use references. If you're new to C++, you can read this StackOverflow question, which introduces them briefly: References in C++.

At any rate, if the SDL function you want to wrap has the signature:

void SDL_foo(SDL_Rect *rect);

then your wrapper would be:

inline void my_wrapper_name(SDL_Rect& rect) {
    SDL_foo(&rect);
}

and that works even if SDL_Rect has only been forward-declared!

Now, if you're sure that the SDL_Rect will:

  1. Never be used except in the call to SDL_foo() and
  2. Not be modified by SDL_foo() despite it not being designated as taking a const SDL_Rect*

you can also write:

inline void my_wrapper_name(const SDL_Rect& rect) {
    SDL_foo(const_cast<SDL_Rect*>(&rect));
}

in which case you would be able, elswhere in your code and with SDL_Rect actually defined, to invoke your wrapper on an initializer list for an SDL_Rect.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • I think you'd need my_wrapper_name() to take a (const SDL_Rect &) if you want to be able pass a temporary object to it. – Jeremy Friesner Jun 22 '18 at 22:01
  • @JeremyFriesner: Well, yes, but - I doubt OP actually want to do that. Let me edit accordingly. – einpoklum Jun 22 '18 at 22:05
  • Sure looks like it to me: "This is so I can simply call them like this: foo({x, y, w, h});" – Jeremy Friesner Jun 22 '18 at 22:16
  • @JeremyFriesner: OP just _thinks_ s/he wants to do that, if `foo()` makes an API call which changes an `SDL_Rect`. If you know what I mean. – einpoklum Jun 23 '18 at 00:08
  • I'm not able to change the calling convention of a third party library like SDL. How could I make this more clear in the future I mentioned it in my post and even tagged it. – Kristopher Ives Jun 26 '18 at 13:54
  • @KristopherIves: I didn't suggest you change the calling conventions of third-party libraries. I just expressed doubt that a library like SDL will not have a const` marker on a pointer parameter whose pointed-to data is not altered. – einpoklum Jun 26 '18 at 16:22