Best Practice for Values Return C/C++

Started by
25 comments, last by Lightness1024 11 years, 2 months ago

Hi,

What's is the best practice for values return in a function in C/C++ for Game Engines??

In C/C++ I can return one value with the sentence return.


Texture = LoadTexture(path);

or I can return the value on second via


LoadTexure(path, &Texture);

In both examples the variable Texture is get by the function LoadTexture.
What's is the better??
Thanks
Advertisement

The latter convention is commonly used when you want to return an error code that tells you exactly what went wrong, the former convention is used when you're happy enough with just knowing if there was an error or not (e.g. return NULL if you couldn't load the texture). Note that in this case you can still make a separate function to retrieve the exact cause if you need.

So it really depends on how you want to handle errors, pretty much.

Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

Almost every function in my engine is of the form:

error = function_name (arg1, arg2, arg3...);

The return value is always an error number. However, if all the function needs to return is a positive integer, you can return that in the return value.

Almost every function call is followed by:

if (error < 0) { return (error); }

... or sometimes some other code or function call might be tried before or instead-of the return(error); portion.

Overall, the following is common (though some elements are not required in many cases).

error = value = function_name (arg1, arg2...); // skip "value =" portion if only error values are returned
if (error < 0) {
free_resources (arg1, arg2, arg3...);
return (error);
}
// continue on with valid positive value

The above approach has proved the most convenient for me over many applications and libraries, after trying all sorts of alternatives. One equally viable approach, and possibly better because it is more consistent, is to never return anything but error values from functions, and remove all values in arguments. I didn't do that, but now and then think that's a better approach (because absolutely uniform in layout).

I prefer "Texture = LoadTexture(path)". It makes it crystal clear that the texture is an output, not an input. It also enables you to write more natural expressions, like using LoadTexture(path) as an argument to another function.

I'm a natural advocate of Stroppy's solution which corresponds better the the equivalent in pseudo code. errors can be handled via exceptions for better messages/codes.

especially now that there is std::move.

I agree with Stroppy and Lightness - returning the value that is expected result of a function (instead of some error) is the way to go. Returning error codes is one of most counterintuitive ways for dealing with things.

Of course it all depends on the case - in your case you just want to return Texture so go with your first suggestion, like:


std::shared_ptr<Texture> texture = textureCache.GetTexture("...");

I can hardly think of situation that would justify what was suggested above about using return value only for error code (and returning objects by assigning to reference). First of all, its quite hard to determine quickly what happens because you may have references that have different meaning: in, out, in/out. There is no special syntax to indicate it so you have to write a description. If you really need several error codes (which is IMO very rare and if you write code that requires it very often, something is wrong.. - there are cases where such coding is probably only way to achieve something, but I doubt that your case belongs there).

If you REALLY need to use this approach, at least:

  • Make sure your references already tell your intentions, so if you're using reference as "in" only, make it const, if you use it for return value don't make it const:
    
    int LoadTexture(const string& name, const Type& type, Texture& texture)
    {
        if (load_from_file(name, type))
        {
            texture = <loaded texture>
            return SUCCESS;
        }
        return FAILURE;
    }
    

    Here references that are only for reading are marked const, while your return reference is not - its easier to know whats going to happen that way.

  • Or use std::tuple and std::tie to return multiple values from a function (for example std::tuple<Texture*, int> where int is error code)

Where are we and when are we and who are we?
How many people in how many places at how many times?

an alternative:

Texture* loadTexture(const wchar_t* name, ErrorCode* error = 0);
Actually I don't think it's even worth talking about error handling in this particular function's interface, because a situation where a built-in texture does not load simply should not happen. If it does happen in a packaged release, then the installation is broken and all bets are off. Just printing a diagnostic message to user and killing the game sounds like a good move at that point instead of propagating the error any higher.

In a situation where it's actually reasonable to expect problems with loading a file - like loading replay files that users are intended to copy manually between each other, and which could end up with messed up permissions which prevent reading - I think it would be a more reasonable approach to first try to open the file, check for success, and then pass the file *handle* to a loader function which is then guaranteed to succeed and doesn't need an error handling interface.

I generally stick with.

int DoSomething(); // return a single value

void DoSomething(int* const result); // multiple value

If you've never seen const in that specific position (between the pointer and name), it means you can change the data but not the pointer (barring any goofy tricks).

Hi,

What's is the best practice for values return in a function in C/C++ for Game Engines??

In C/C++ I can return one value with the sentence return.




Texture = LoadTexture(path);

or I can return the value on second via




LoadTexure(path, &Texture);

In both examples the variable Texture is get by the function LoadTexture.
What's is the better??
Thanks

When you return by reference, the value is directly written to the variable and you effectively avoid creating a temporary object that will be copied to your variable. The cost of creating that temporary object and the cost of the copy could be potentially be a performance hit. So to answer your question, it is good practice to return by reference wherever possible.

This topic is closed to new replies.

Advertisement