Migrate to open-source absl::StatusOr<>

This removes our own fork of `absl::StatusOr<>`. Sandboxed API still includes
a custom matcher for Googletest, as that is not open source yet. For
compatibility, the `statusor.h` header is still retained and now aliases
`sapi::StatusOr<>` to `absl::StatusOr<>`.

PiperOrigin-RevId: 329916309
Change-Id: I0544b73a9e312dce499bc4128c28457e04ab9929
This commit is contained in:
Christian Blichmann 2020-09-03 07:40:09 -07:00 committed by Copybara-Service
parent 23da55c19a
commit fdf0483ca0
18 changed files with 81 additions and 285 deletions

View File

@ -18,7 +18,7 @@ project(absl-download NONE)
include(ExternalProject)
ExternalProject_Add(absl
GIT_REPOSITORY https://github.com/abseil/abseil-cpp
GIT_TAG 6e18c7115df9b7ca0987cc346b1b1d4b3cc829b3 # 2020-04-28
GIT_TAG 0e9921b75a0fdd639a504ec8443fc1fe801becd7 # 2020-09-02
SOURCE_DIR "${CMAKE_BINARY_DIR}/absl-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/absl-build"
CONFIGURE_COMMAND ""

View File

@ -43,6 +43,8 @@ add_library(sapi_embed_file STATIC
add_library(sapi::embed_file ALIAS sapi_embed_file)
target_link_libraries(sapi_embed_file PRIVATE
absl::flat_hash_map
absl::status
absl::statusor
absl::strings
absl::synchronization
glog::glog
@ -65,6 +67,8 @@ add_library(sapi::sapi ALIAS sapi_sapi)
target_link_libraries(sapi_sapi
PRIVATE absl::flat_hash_map
absl::memory
absl::status
absl::statusor
absl::str_format
absl::strings
absl::synchronization
@ -76,7 +80,6 @@ target_link_libraries(sapi_sapi
sandbox2::strerror
sandbox2::util
sapi::embed_file
sapi::status
sapi::vars
PUBLIC absl::core_headers
sandbox2::client
@ -137,6 +140,8 @@ add_library(sapi_vars STATIC
add_library(sapi::vars ALIAS sapi_vars)
target_link_libraries(sapi_vars PRIVATE
absl::core_headers
absl::status
absl::statusor
absl::str_format
absl::strings
absl::synchronization
@ -147,7 +152,6 @@ target_link_libraries(sapi_vars PRIVATE
sapi::lenval_core
sapi::proto_arg_proto
sapi::status
sapi::statusor
sapi::var_type
)
@ -178,6 +182,7 @@ if(SAPI_ENABLE_TESTS)
)
target_link_libraries(sapi_test PRIVATE
absl::memory
absl::status
benchmark
sapi::sapi
sapi::status

View File

@ -34,9 +34,9 @@ def sapi_deps():
maybe(
http_archive,
name = "com_google_absl",
sha256 = "6668ada01192e2b95b42bb3668cfa5282c047de5176f5e567028e12f8bfb8aef", # 2020-04-28
strip_prefix = "abseil-cpp-6e18c7115df9b7ca0987cc346b1b1d4b3cc829b3",
urls = ["https://github.com/abseil/abseil-cpp/archive/6e18c7115df9b7ca0987cc346b1b1d4b3cc829b3.zip"],
sha256 = "8061df0ebbd3f599bcd3f5e57fb8003564d50a9b6a81a7f968fb0196b952365d", # 2020-09-02
strip_prefix = "abseil-cpp-0e9921b75a0fdd639a504ec8443fc1fe801becd7",
urls = ["https://github.com/abseil/abseil-cpp/archive/0e9921b75a0fdd639a504ec8443fc1fe801becd7.zip"],
)
maybe(
http_archive,

View File

@ -32,11 +32,11 @@ add_executable(main_zlib
main_zlib.cc
)
target_link_libraries(main_zlib PRIVATE
absl::status
sapi::base
glog::glog
sapi::flags
sapi::sapi
sapi::status
sapi::statusor
sapi::zlib_sapi
)

View File

@ -271,6 +271,8 @@ target_link_libraries(sandbox2_sandbox2
absl::flat_hash_set
absl::memory
absl::optional
absl::status
absl::statusor
absl::str_format
absl::strings
absl::synchronization
@ -300,7 +302,6 @@ target_link_libraries(sandbox2_sandbox2
sandbox2::util
sandbox2::violation_proto
sapi::base
sapi::statusor
PUBLIC sapi::flags
sapi::status
sandbox2::logsink
@ -351,6 +352,8 @@ add_library(sandbox2_forkserver STATIC
add_library(sandbox2::forkserver ALIAS sandbox2_forkserver)
target_link_libraries(sandbox2_forkserver PRIVATE
absl::memory
absl::status
absl::statusor
absl::str_format
absl::strings
libcap::libcap
@ -369,7 +372,6 @@ target_link_libraries(sandbox2_forkserver PRIVATE
sandbox2::util
sapi::base
sapi::raw_logging
sapi::statusor
)
# sandboxed_api/sandbox2:fork_client
@ -397,6 +399,7 @@ target_link_libraries(sandbox2_mounts PRIVATE
absl::core_headers
absl::flat_hash_set
absl::status
absl::statusor
absl::str_format
absl::strings
protobuf::libprotobuf
@ -408,7 +411,6 @@ target_link_libraries(sandbox2_mounts PRIVATE
sapi::base
sapi::raw_logging
sapi::status
sapi::statusor
)
# sandboxed_api/sandbox2:namespace
@ -463,8 +465,8 @@ target_link_libraries(sandbox2_util
sandbox2::strerror
sapi::base
sapi::raw_logging
sapi::statusor
PUBLIC absl::status
absl::statusor
)
target_compile_options(sandbox2_util PRIVATE
# The default is 16384, however we need to do a clone with a
@ -482,12 +484,13 @@ add_library(sandbox2::buffer ALIAS sandbox2_buffer)
target_link_libraries(sandbox2_buffer PRIVATE
absl::core_headers
absl::memory
absl::status
absl::statusor
absl::strings
sandbox2::strerror
sandbox2::util
sapi::base
sapi::status
sapi::statusor
)
# sandboxed_api/sandbox2:forkserver_proto
@ -527,6 +530,8 @@ add_library(sandbox2_comms STATIC
add_library(sandbox2::comms ALIAS sandbox2_comms)
target_link_libraries(sandbox2_comms
PRIVATE absl::memory
absl::status
absl::statusor
absl::str_format
absl::strings
sandbox2::strerror
@ -534,7 +539,6 @@ target_link_libraries(sandbox2_comms
sapi::base
sapi::raw_logging
sapi::status_proto
sapi::statusor
PUBLIC absl::core_headers
absl::status
absl::synchronization
@ -832,6 +836,7 @@ if(SAPI_ENABLE_TESTS)
)
target_link_libraries(stack_trace_test PRIVATE
absl::memory
absl::status
absl::strings
sandbox2::bpf_helper
sandbox2::fileops

View File

@ -27,7 +27,6 @@ cc_binary(
deps = [
"//sandboxed_api/sandbox2",
"//sandboxed_api/sandbox2:comms",
"//sandboxed_api/sandbox2/network_proxy:filtering",
"//sandboxed_api/sandbox2/util:bpf_helper",
"//sandboxed_api/sandbox2/util:fileops",
"//sandboxed_api/sandbox2/util:runfiles",
@ -50,6 +49,7 @@ cc_binary(
"//sandboxed_api/util:status",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)

View File

@ -31,13 +31,14 @@ target_link_libraries(sandbox2_networkproxy_sandbox PRIVATE
sapi::flags
)
# sandboxed_api/sandbox2/examples/networkproxy:networkproxy_bin
add_executable(sandbox2_networkproxy_bin
networkproxy_bin.cc
)
add_executable(sandbox2::networkproxy_bin ALIAS sandbox2_networkproxy_bin)
target_link_libraries(sandbox2_networkproxy_bin PRIVATE
absl::status
absl::statusor
absl::str_format
glog::glog
gflags::gflags
@ -48,6 +49,4 @@ target_link_libraries(sandbox2_networkproxy_bin PRIVATE
sapi::base
sapi::flags
sapi::status
sapi::statusor
)

View File

@ -14,6 +14,7 @@
#include "sandboxed_api/util/flag.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "sandboxed_api/sandbox2/client.h"
#include "sandboxed_api/sandbox2/comms.h"

View File

@ -33,14 +33,14 @@ add_library(sandbox2_network_proxy_filtering STATIC
filtering.h
)
add_library(sandbox2::network_proxy_filtering ALIAS sandbox2_network_proxy_filtering)
target_link_libraries(sandbox2_network_proxy_filtering PRIVATE
absl::memory
glog::glog
sandbox2::comms
sandbox2::fileops
sapi::base
target_link_libraries(sandbox2_network_proxy_filtering
PRIVATE absl::memory
absl::status
glog::glog
sandbox2::comms
sandbox2::fileops
sapi::base
PUBLIC sapi::status
sapi::statusor
)
# sandboxed_api/sandbox2/network_proxy:client

View File

@ -41,7 +41,12 @@ STATIC_LINKOPTS = [
]
# TODO(https://github.com/bazelbuild/bazel/issues/8672): Remove this workaround
EXTRA_FULLY_STATIC_LINKOPTS = ["-l:libstdc++.a"]
# Change is scheduled for Bazel 4.0. Specifying
# `--incompatible_linkopts_to_linklibs` also works
EXTRA_FULLY_STATIC_LINKOPTS = [
"-l:libstdc++.a",
"-l:libm.a",
]
cc_binary(
name = "abort",

View File

@ -80,7 +80,6 @@ target_link_libraries(sandbox2_util_minielf PRIVATE
sandbox2::util
sapi::base
sapi::raw_logging
sapi::statusor
)
# sandboxed_api/sandbox2/util:temp_file
@ -95,7 +94,7 @@ target_link_libraries(sandbox2_util_temp_file
sandbox2::strerror
sapi::base
PUBLIC absl::status
sapi::statusor
absl::statusor
)
# sandboxed_api/sandbox2/util:maps_parser
@ -106,9 +105,9 @@ add_library(sandbox2_util_maps_parser STATIC
add_library(sandbox2::maps_parser ALIAS sandbox2_util_maps_parser)
target_link_libraries(sandbox2_util_maps_parser PRIVATE
absl::status
absl::statusor
absl::strings
sapi::base
sapi::statusor
)
# sandboxed_api/sandbox2/util:runfiles

View File

@ -23,8 +23,8 @@
#include "sandboxed_api/sandbox2/testing.h"
#include "sandboxed_api/sandbox2/util/path.h"
using testing::Gt;
using testing::IsTrue;
using ::testing::Gt;
using ::testing::IsTrue;
namespace sandbox2 {
namespace util {

View File

@ -45,20 +45,15 @@ cc_library(
cc_library(
name = "statusor",
hdrs = ["statusor.h"],
copts = sapi_platform_copts(),
visibility = ["//visibility:public"],
deprecation = "Migrate to `absl::StatusOr<T>`",
deps = [
":raw_logging",
":status",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:log_severity",
"@com_google_absl//absl/status",
"@com_google_absl//absl/types:variant",
"@com_google_absl//absl/status:statusor",
],
)
# gMock matchers for absl::Status and sapi::StatusOr<T> and a gUnit printer
# extension for sapi::StatusOr<T>.
# gMock matchers for absl::Status and absl::StatusOr<T> and a gUnit printer
# extension. Adapted from the version in Asylo.
cc_library(
name = "status_matchers",
testonly = 1,
@ -67,8 +62,8 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
":status",
":statusor",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/types:optional",
"@com_google_googletest//:gtest",
],
@ -87,19 +82,6 @@ cc_test(
],
)
# Tests for the StatusOr template class.
cc_test(
name = "statusor_test",
srcs = ["statusor_test.cc"],
copts = sapi_platform_copts(),
deps = [
":status",
":status_matchers",
":statusor",
"@com_google_googletest//:gtest_main",
],
)
# Tests for the Status macros.
cc_test(
name = "status_macros_test",
@ -108,9 +90,9 @@ cc_test(
deps = [
":status",
":status_matchers",
":statusor",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],

View File

@ -48,11 +48,10 @@ add_library(sapi_util_statusor STATIC
)
add_library(sapi::statusor ALIAS sapi_util_statusor)
target_link_libraries(sapi_util_statusor PRIVATE
absl::base
absl::core_headers
absl::variant
sapi::raw_logging
sapi::status
absl::status
absl::statusor
sapi::base
)
# sandboxed_api/util:flag
@ -90,7 +89,6 @@ if(SAPI_ENABLE_TESTS)
sapi::base
PUBLIC absl::status
sapi::status
sapi::statusor
)
# sandboxed_api/util:status_test
@ -98,20 +96,24 @@ if(SAPI_ENABLE_TESTS)
status_test.cc
)
target_link_libraries(status_test PRIVATE
sapi::status
sapi::status_matchers
sapi::test_main
absl::status
absl::type_traits
)
gtest_discover_tests(status_test)
# sandboxed_api/util:statusor_test
add_executable(statusor_test
statusor_test.cc
# sandboxed_api/util:status_macros_test
add_executable(status_macros_test
status_macros_test.cc
)
target_link_libraries(statusor_test PRIVATE
target_link_libraries(status_macros_test PRIVATE
sapi::status_matchers
sapi::test_main
absl::status
absl::statusor
absl::type_traits
)
gtest_discover_tests(statusor_test)
gtest_discover_tests(status_macros_test)
endif()

View File

@ -42,6 +42,6 @@
if (ABSL_PREDICT_FALSE(!statusor.ok())) { \
return statusor.status(); \
} \
lhs = std::move(statusor).ValueOrDie();
lhs = std::move(statusor).value();
#endif // THIRD_PARTY_SAPI_UTIL_STATUS_MACROS_H_

View File

@ -20,10 +20,10 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "sandboxed_api/util/status.h"
#include "sandboxed_api/util/status_matchers.h"
#include "sandboxed_api/util/statusor.h"
namespace sapi {
namespace {
@ -52,17 +52,17 @@ TEST(ReturnIfError, ReturnsOnErrorFromLambda) {
TEST(AssignOrReturn, AssignsMultipleVariablesInSequence) {
auto func = []() -> absl::Status {
int value1;
SAPI_ASSIGN_OR_RETURN(value1, StatusOr<int>(1));
SAPI_ASSIGN_OR_RETURN(value1, absl::StatusOr<int>(1));
EXPECT_EQ(1, value1);
int value2;
SAPI_ASSIGN_OR_RETURN(value2, StatusOr<int>(2));
SAPI_ASSIGN_OR_RETURN(value2, absl::StatusOr<int>(2));
EXPECT_EQ(2, value2);
int value3;
SAPI_ASSIGN_OR_RETURN(value3, StatusOr<int>(3));
SAPI_ASSIGN_OR_RETURN(value3, absl::StatusOr<int>(3));
EXPECT_EQ(3, value3);
int value4;
SAPI_ASSIGN_OR_RETURN(value4,
StatusOr<int>(absl::UnknownError("EXPECTED")));
absl::StatusOr<int>(absl::UnknownError("EXPECTED")));
return absl::UnknownError(absl::StrCat("ERROR: assigned value ", value4));
};
@ -72,11 +72,12 @@ TEST(AssignOrReturn, AssignsMultipleVariablesInSequence) {
TEST(AssignOrReturn, AssignsRepeatedlyToSingleVariable) {
auto func = []() -> absl::Status {
int value = 1;
SAPI_ASSIGN_OR_RETURN(value, StatusOr<int>(2));
SAPI_ASSIGN_OR_RETURN(value, absl::StatusOr<int>(2));
EXPECT_EQ(2, value);
SAPI_ASSIGN_OR_RETURN(value, StatusOr<int>(3));
SAPI_ASSIGN_OR_RETURN(value, absl::StatusOr<int>(3));
EXPECT_EQ(3, value);
SAPI_ASSIGN_OR_RETURN(value, StatusOr<int>(absl::UnknownError("EXPECTED")));
SAPI_ASSIGN_OR_RETURN(value,
absl::StatusOr<int>(absl::UnknownError("EXPECTED")));
return absl::UnknownError("ERROR");
};
@ -87,7 +88,7 @@ TEST(AssignOrReturn, MovesUniquePtr) {
auto func = []() -> absl::Status {
std::unique_ptr<int> ptr;
SAPI_ASSIGN_OR_RETURN(
ptr, StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(1)));
ptr, absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(1)));
EXPECT_EQ(*ptr, 1);
return absl::UnknownError("EXPECTED");
};
@ -98,8 +99,8 @@ TEST(AssignOrReturn, MovesUniquePtr) {
TEST(AssignOrReturn, DoesNotAssignUniquePtrOnErrorStatus) {
auto func = []() -> absl::Status {
std::unique_ptr<int> ptr;
SAPI_ASSIGN_OR_RETURN(
ptr, StatusOr<std::unique_ptr<int>>(absl::UnknownError("EXPECTED")));
SAPI_ASSIGN_OR_RETURN(ptr, absl::StatusOr<std::unique_ptr<int>>(
absl::UnknownError("EXPECTED")));
EXPECT_EQ(ptr, nullptr);
return absl::OkStatus();
};
@ -111,10 +112,10 @@ TEST(AssignOrReturn, MovesUniquePtrRepeatedlyToSingleVariable) {
auto func = []() -> absl::Status {
std::unique_ptr<int> ptr;
SAPI_ASSIGN_OR_RETURN(
ptr, StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(1)));
ptr, absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(1)));
EXPECT_EQ(*ptr, 1);
SAPI_ASSIGN_OR_RETURN(
ptr, StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(2)));
ptr, absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(2)));
EXPECT_EQ(*ptr, 2);
return absl::UnknownError("EXPECTED");
};

View File

@ -19,9 +19,9 @@
#include "gmock/gmock.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/optional.h"
#include "sandboxed_api/util/status_macros.h"
#include "sandboxed_api/util/statusor.h"
#define SAPI_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
SAPI_ASSERT_OK_AND_ASSIGN_IMPL( \

View File

@ -12,220 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// This file and it's implementation provide a custom fork of
// util/task/statusor.h. This will become obsolete and will be replaced once
// Abseil releases absl::Status.
#ifndef THIRD_PARTY_SAPI_UTIL_STATUSOR_H_
#define THIRD_PARTY_SAPI_UTIL_STATUSOR_H_
#include <initializer_list>
#include <utility>
#include "absl/base/internal/raw_logging.h"
#include "absl/base/attributes.h"
#include "absl/base/log_severity.h"
#include "absl/status/status.h"
#include "absl/types/variant.h"
#include "sandboxed_api/util/raw_logging.h"
#include "absl/status/statusor.h"
namespace sapi {
template <typename T>
class ABSL_MUST_USE_RESULT StatusOr {
template <typename U>
friend class StatusOr;
public:
using element_type = T;
explicit StatusOr() : variant_(absl::UnknownError("")) {}
StatusOr(const StatusOr&) = default;
StatusOr& operator=(const StatusOr&) = default;
StatusOr(StatusOr&&) = default;
StatusOr& operator=(StatusOr&&) = default;
// Not implemented:
// template <typename U> StatusOr(const StatusOr<U>& other)
// template <typename U> StatusOr(StatusOr<U>&& other)
template <typename U>
StatusOr& operator=(const StatusOr<U>& other) {
if (other.ok()) {
variant_ = other.value();
} else {
variant_ = other.status();
}
return *this;
}
template <typename U>
StatusOr& operator=(StatusOr<U>&& other) {
if (other.ok()) {
variant_ = std::move(other).value();
} else {
variant_ = std::move(other).status();
}
return *this;
}
StatusOr(const T& value) : variant_(value) {}
StatusOr(const absl::Status& status) : variant_(status) { EnsureNotOk(); }
// Not implemented:
// template <typename U = T> StatusOr& operator=(U&& value)
StatusOr(T&& value) : variant_(std::move(value)) {}
StatusOr(absl::Status&& value) : variant_(std::move(value)) {}
StatusOr& operator=(absl::Status&& status) {
variant_ = std::move(status);
EnsureNotOk();
}
template <typename... Args>
explicit StatusOr(absl::in_place_t, Args&&... args)
: StatusOr(T(std::forward<Args>(args)...)) {}
template <typename U, typename... Args>
explicit StatusOr(absl::in_place_t, std::initializer_list<U> ilist,
Args&&... args)
: StatusOr(ilist, U(std::forward<Args>(args)...)) {}
explicit operator bool() const { return ok(); }
ABSL_MUST_USE_RESULT bool ok() const {
return absl::holds_alternative<T>(variant_);
}
const absl::Status& status() const& {
static const auto* ok_status = new absl::Status();
return ok() ? *ok_status : absl::get<absl::Status>(variant_);
}
absl::Status status() && {
return ok() ? absl::OkStatus()
: std::move(absl::get<absl::Status>(variant_));
}
const T& value() const& {
EnsureOk();
return absl::get<T>(variant_);
}
T& value() & {
EnsureOk();
return absl::get<T>(variant_);
}
const T&& value() const&& {
EnsureOk();
return absl::get<T>(std::move(variant_));
}
T&& value() && {
EnsureOk();
return absl::get<T>(std::move(variant_));
}
const T& ValueOrDie() const& {
EnsureOk();
return absl::get<T>(variant_);
}
T& ValueOrDie() & {
EnsureOk();
return absl::get<T>(variant_);
}
T&& ValueOrDie() && {
EnsureOk();
return absl::get<T>(std::move(variant_));
}
const T& operator*() const& {
EnsureOk();
return absl::get<T>(variant_);
}
T& operator*() & {
EnsureOk();
return absl::get<T>(variant_);
}
const T&& operator*() const&& {
EnsureOk();
return absl::get<T>(std::move(variant_));
}
T&& operator*() && {
EnsureOk();
return absl::get<T>(std::move(variant_));
}
const T* operator->() const {
EnsureOk();
return &absl::get<T>(variant_);
}
T* operator->() {
EnsureOk();
return &absl::get<T>(variant_);
}
template <typename U>
T value_or(U&& default_value) const& {
if (ok()) {
return absl::get<T>(variant_);
}
return std::forward<U>(default_value);
}
template <typename U>
T value_or(U&& default_value) && {
if (ok()) {
return absl::get<T>(std::move(variant_));
}
return std::forward<U>(default_value);
}
void IgnoreError() const { /* no-op */
}
template <typename... Args>
T& emplace(Args&&... args) {
return variant_.template emplace<T>(std::forward<Args>(args)...);
}
template <typename U, typename... Args>
T& emplace(std::initializer_list<U> ilist, Args&&... args) {
return variant_.template emplace<T>(ilist, std::forward<Args>(args)...);
}
private:
void EnsureOk() const {
if (!ok()) {
// GoogleTest needs this exact error message for death tests to work.
SAPI_RAW_LOG(FATAL,
"Attempting to fetch value instead of handling error %s",
status().message());
}
}
void EnsureNotOk() const {
if (ok()) {
SAPI_RAW_LOG(
FATAL,
"An OK status is not a valid constructor argument to StatusOr<T>");
}
}
absl::variant<absl::Status, T> variant_;
};
using StatusOr ABSL_DEPRECATED("Use absl::StatusOr instead") =
absl::StatusOr<T>;
} // namespace sapi