So, I have been thinking about a system/library to help create and manipulate result information, the main goal is to create a simple API capable of create and gather result information, also supported by macro as syntax sugar and it needs to be flexible.

My idea is make it as const as possible, use result type as function pointer, that way we can also see information in debug mode and just call the constexpr result function in order to get result information, something like this:

using ResultType = uint32_t(*)();

constexpr uint32_t ResultOk(){return 0;}
constexpr uint32_t ResultFail(){return 1;}

bool CheckIntZero(int Number, ResultType& Result = *(ResultType*)&ResultOk)
{
    Result = Number != 0 ? ResultOk : ResultFail;
    return Result == ResultOk;
}

This is a veery simple example to sell the idea of how would work. The difference would be the return type of the constexpr function, which will be string_views for text information (the id won’t be required since the actual function pointer will be used as id).

I will do a simple test in the quick bench to check and compare with the example below if it is fast enough:

using ResultType = uint32_t;

uint32_t ResultOk=0;
uint32_t ResultFail=1;

bool CheckIntZero(int Number, ResultType& Result = ResultOk)
{
    Result = Number != 0 ? ResultOk : ResultFail;
    return Result == ResultOk;
}

And they are equivalent in performance.

Quickbench Link

For the actual result information, the first example will be instantiate always when the result function is called, but for the second example, it will have a unordered_map with the result info inserted before the computations.

So fixing the code for example 1:

struct ResultInfo
{
    std::string_view Name, Description, ErrorMessage;
};

using ResultType = ResultInfo(*)();

constexpr ResultInfo ResultOk(){return {"Ok", "No error.", "No error."};}
constexpr ResultInfo ResultFail(){return {"Fail", "Generic fail.", "An error has been occured."};}

bool CheckIntZero(int Number, ResultType& Result = *(ResultType*)&ResultOk)
{
    Result = Number != 0 ? ResultOk : ResultFail;
    return Result == ResultOk;
}

And fixing the code for example 2:

struct ResultInfo
{
    std::string_view Name, Description, ErrorMessage;
};

using ResultType = uint32_t;

ResultType ResultOk=0;
ResultType ResultFail=1;

std::unordered_map<ResultType, ResultInfo> gResults{};

bool CheckIntZero(int Number, ResultType& Result = ResultOk)
{
    Result = Number != 0 ? ResultOk : ResultFail;
    return Result == ResultOk;
}

struct Reg
{
  Reg()
  {
    gResults.insert({ResultOk, {"Ok", "No error.", "No error."}});
    gResults.insert({ResultFail, {"Fail", "Generic fail.", "An error has been occured."}});
  }
};
static Reg gReg{};

This time I will get the info in the quick bench.

I’m not surprised that example 1 is the fastest.

Quickbench Link

Conclusion: with this way we can use a flexible result system, really cheap and fast without using enumeration but with sort of debug features of one. Maybe using natvis to clear the mangled text of the function pointer.

Github Link

Leave a Reply

Your email address will not be published. Required fields are marked *