Skip to content

Commit

Permalink
retval<T> to return a value and an error number at the same time (ali…
Browse files Browse the repository at this point in the history
  • Loading branch information
lihuiba authored Apr 3, 2024
1 parent 453fa01 commit 8ac6741
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 0 deletions.
23 changes: 23 additions & 0 deletions common/alog.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ limitations under the License.

#include <photon/common/utility.h>
#include <photon/common/conststr.h>
#include <photon/common/retval.h>

class ILogOutput {
protected:
Expand Down Expand Up @@ -508,6 +509,21 @@ struct ERRNO

LogBuffer& operator << (LogBuffer& log, ERRNO e);

inline LogBuffer& operator << (LogBuffer& log, const photon::retval_base& rvb) {
auto x = rvb._errno;
return x ? (log << ERRNO((int)x)) : log;
}

template<typename T> inline
LogBuffer& operator << (LogBuffer& log, const photon::retval<T>& v) {
return v.succeeded() ? (log << v.get()) : (log << v.base());
}

template<> inline
LogBuffer& operator << <void> (LogBuffer& log, const photon::retval<void>& v) {
return log << v.base();
}

template<typename T>
struct NamedValue
{
Expand Down Expand Up @@ -553,6 +569,13 @@ inline LogBuffer& operator<<(LogBuffer& log, const NamedValue<T>& v) {
return retv; \
}

#define LOG_ERROR_RETVAL(error, ...) { \
retval_base e{error}; \
assert(e.failed()); \
LOG_ERROR(__VA_ARGS__, ' ', e); \
return e; \
}

// Acts like a LogBuilder
// but able to do operations when log builds
template <typename Builder, typename Append>
Expand Down
94 changes: 94 additions & 0 deletions common/retval.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
Copyright 2022 The Photon Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once
#include <inttypes.h>
#include <assert.h>

namespace photon {

struct retval_base {
// use uint64_t to make sure the result is returned
// via another register, so that it is accessed easily
uint64_t _errno = 0;
bool failed() const { return _errno; }
bool succeeded() const { return !failed(); }
int get_errno() const { assert(_errno > 0); return (int)_errno; }
};

template<typename T> inline
T failure_value() { return 0; }

template<typename T>
struct retval : public retval_base {
T _val;
retval(T x) : _val(x) { }
retval(int _errno, T val) : retval_base{(uint64_t)_errno}, _val(val) {
assert(failed());
}
retval(const retval_base& rvb) : retval_base(rvb) {
assert(failed());
_val = failure_value<T>();
}
operator T() const {
return get();
}
T get() const {
return _val;
}
retval_base base() const {
return *this;
}
bool operator==(const retval& rhs) const {
return _errno ? (_errno == rhs._errno) : (_val == rhs._val);
}
bool operator!=(const retval& rhs) const {
return !(*this == rhs);
}
bool operator==(T rhs) const {
return _val == rhs;
}
bool operator!=(T rhs) const {
return _val != rhs;
}
};

template<>
struct retval<void> : public retval_base {
retval(int _errno = 0) : retval_base{(uint64_t)_errno} { }
retval(const retval_base& rvb) : retval_base(rvb) { }
void get() const { }
retval_base base() const {
return *this;
}
bool operator==(const retval& rhs) const {
return _errno == rhs._errno;
}
bool operator!=(const retval& rhs) const {
return !(*this == rhs);
}
};

}

#define DEFINE_FAILURE_VALUE(type, val) namespace photon { \
template<> inline type failure_value<type>() { return val; } }

DEFINE_FAILURE_VALUE(int8_t, -1)
DEFINE_FAILURE_VALUE(int16_t, -1)
DEFINE_FAILURE_VALUE(int32_t, -1)
DEFINE_FAILURE_VALUE(int64_t, -1)

50 changes: 50 additions & 0 deletions common/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ limitations under the License.
#include "../string-keyed.h"
#include "../range-lock.h"
#include "../expirecontainer.h"
#include "../retval.h"
#include <photon/thread/timer.h>
#include <photon/thread/thread11.h>

Expand Down Expand Up @@ -883,6 +884,55 @@ TEST(generator, example)
___example_of_generator____();
}

retval<double> bar() {
return {EALREADY, 0}; // return a failure
}

photon::retval<int> foo(int i) {
switch (i) {
default:
return 32;
case 1:
return retval_base{EINVAL};
case 2:
LOG_ERROR_RETVAL(EADDRINUSE, "trying to use LOG_ERROR_RETVAL() with an error number constant");
case 3:
retval<double> ret = bar();
EXPECT_TRUE(ret.failed());
// pass on ONLY the error number
LOG_ERROR_RETVAL(ret, "trying to pass on an existing (failed) retval<double> to retval<int>");
}
}

retval<void> ret_failed() {
return {EBADF};
}

retval<void> ret_succeeded() {
return {/* 0 */};
}

TEST(retval, basic) {
const static retval<int> rvs[] =
{{32}, {EINVAL, -2345}, {EADDRINUSE, -1234}, {EALREADY, -5234}};
EXPECT_EQ(rvs[0], 32);
EXPECT_EQ(rvs[1], -2345);
EXPECT_EQ(rvs[2], -1234);
EXPECT_EQ(rvs[3], -5234);

for (int i = 0; i < LEN(rvs); ++i) {
auto ret = foo(i);
LOG_DEBUG("got ", ret);
EXPECT_EQ(ret, rvs[i]);
}
auto A = ret_failed();
EXPECT_TRUE(A.failed());
LOG_DEBUG(A);
auto B = ret_succeeded();
EXPECT_TRUE(B.succeeded());
LOG_DEBUG(B);
}

template <class T>
void basic_map_test(T &test_map) {

Expand Down
1 change: 1 addition & 0 deletions include/photon/common/retval.h

0 comments on commit 8ac6741

Please sign in to comment.