Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added iOS C++ Common Utils #866

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions tensorflow_lite_support/ios/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,24 @@ objc_library(
],
)

objc_library(
name = "TFLCommonCppUtils",
srcs = [
"sources/TFLCommonCppUtils.mm",
],
hdrs = [
"sources/TFLCommonCppUtils.h",
],
module_name = "TFLCommonCppUtils",
deps = [
"//tensorflow_lite_support/cc:common",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:cord",
"//tensorflow_lite_support/ios:TFLCommonUtils",
],
)

# bazel build -c opt --config=ios_arm64 //tensorflow_lite_support/ios:TensorFlowLiteTaskVision_framework
ios_static_framework(
name = "TensorFlowLiteTaskVision_framework",
Expand Down
4 changes: 4 additions & 0 deletions tensorflow_lite_support/ios/sources/TFLCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ typedef NS_ENUM(NSUInteger, TFLSupportErrorCode) {
*/
TFLSupportErrorCodeImageProcessingBackendError,

TFLErrorCodeFirst = TFLSupportErrorCodeUnspecifiedError,

TFLErrorCodeLast = TFLSupportErrorCodeImageProcessingBackendError,

/** kNotFound indicates some requested entity (such as a file or directory) was not found. */
TFLSupportErrorCodeNotFoundError = 900,

Expand Down
37 changes: 37 additions & 0 deletions tensorflow_lite_support/ios/sources/TFLCommonCppUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/
#import <Foundation/Foundation.h>
#import "tensorflow_lite_support/ios/sources/TFLCommonUtils.h"

#include "absl/status/status.h"

NS_ASSUME_NONNULL_BEGIN

/** Helper utility for the all tasks which encapsulates common functionality of the iOS task library
* backed by C++ APIs. */
@interface TFLCommonCppUtils : TFLCommonUtils

/**
* Converts an absl status to an NSError.
*
* @param status absl status.
* @param error Pointer to the memory location where the created error should be saved. If `nil`,
* no error will be saved.
*/
+ (BOOL)checkCppError:(const absl::Status &)status toError:(NSError **)error;

@end

NS_ASSUME_NONNULL_END
106 changes: 106 additions & 0 deletions tensorflow_lite_support/ios/sources/TFLCommonCppUtils.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2021 The TensorFlow Authors. All Rights Reserved.
//
// 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.

#import "tensorflow_lite_support/ios/sources/TFLCommonCppUtils.h"
#import "tensorflow_lite_support/ios/sources/TFLCommon.h"

#include <string>

#include "absl/status/status.h" // from @com_google_absl
#include "absl/strings/cord.h" // from @com_google_absl
#include "tensorflow_lite_support/cc/common.h"

@implementation TFLCommonCppUtils

+ (BOOL)checkCError:(TfLiteSupportError *)supportError toError:(NSError **)error {
if (!supportError) {
return YES;
}
NSString *description = [NSString stringWithCString:supportError->message
encoding:NSUTF8StringEncoding];
[TFLCommonUtils createCustomError:error withCode:supportError->code description:description];
return NO;
}

+ (BOOL)checkCppError:(const absl::Status &)status toError:(NSError *_Nullable *)error {
if (!status.ok()) {
return YES;
}
// Payload of absl::Status created by the tflite task library stores an appropriate value of the
// enum TfLiteSupportStatus. The integer value corresponding to the TfLiteSupportStatus enum
// stored in the payload is extracted here to later map to the appropriate error code to be
// returned. In cases where the enum is not stored in (payload is NULL or the payload string
// cannot be converted to an integer), we set the error code value to be 1
// (TFLSupportErrorCodeUnspecifiedError of TFLSupportErrorCode used in the iOS library to signify
// any errors not falling into other categories.) Since payload is of type absl::Cord that can be
// type cast into an absl::optional<std::string>, we use the std::stoi function to convert it into
// an integer code if possible.
NSUInteger generic_error_code = TFLSupportErrorCodeUnspecifiedError;
NSUInteger error_code;
try {
// Try converting payload to integer if payload is not empty. Otherwise convert a string
// signifying generic error code TFLSupportErrorCodeUnspecifiedError to integer.
error_code =
(NSUInteger)std::stoi(static_cast<absl::optional<std::string>>(
status.GetPayload(tflite::support::kTfLiteSupportPayload))
.value_or(std::to_string(generic_error_code)));
} catch (std::invalid_argument &e) {
// If non empty payload string cannot be converted to an integer. Set error code to 1(kError).
error_code = TFLSupportErrorCodeUnspecifiedError;
}

// If error_code is outside the range of enum values possible or is
// TFLSupportErrorCodeUnspecifiedError, we try to map the absl::Status::code() to assign
// appropriate TFLSupportErrorCode or TFLSupportErrorCodeUnspecifiedError in default cases. Note:
// The mapping to absl::Status::code() is done to generate a more specific error code than
// TFLSupportErrorCodeUnspecifiedError in cases when the payload can't be mapped to
// TfLiteSupportStatus. This can happen when absl::Status returned by TfLite are in turn returned
// without moodification by TfLite Support Methods.
if (error_code > TFLErrorCodeLast || error_code <= TFLErrorCodeFirst) {
switch (status.code()) {
case absl::StatusCode::kInternal:
error_code = TFLSupportErrorCodeInternalError;
break;
case absl::StatusCode::kInvalidArgument:
error_code = TFLSupportErrorCodeInvalidArgumentError;
break;
case absl::StatusCode::kNotFound:
error_code = TFLSupportErrorCodeNotFoundError;
break;
default:
error_code = TFLSupportErrorCodeUnspecifiedError;
break;
}
}

// Creates the NSEror with the appropriate error
// TFLSupportErrorCode and message. TFLSupportErrorCode has a one to one
// mapping with TfLiteSupportStatus starting from the value 1(TFLSupportErrorCodeUnspecifiedError)
// and hence will be correctly initialized if directly cast from the integer code derived from
// TfLiteSupportStatus stored in payload. TFLSupportErrorCode omits kOk = 0 of
// TfLiteSupportStatus.
//
// Stores a string including absl status code and message(if non empty) as the
// error message See
// https://github.com/abseil/abseil-cpp/blob/master/absl/status/status.h#L514
// for explanation. absl::Status::message() can also be used but not always
// guaranteed to be non empty.
NSString *description = [NSString
stringWithCString:status.ToString(absl::StatusToStringMode::kWithNoExtraData).c_str()
encoding:NSUTF8StringEncoding];
[TFLCommonUtils createCustomError:error withCode:error_code description:description];
return NO;
}

@end
7 changes: 5 additions & 2 deletions tensorflow_lite_support/ios/sources/TFLCommonUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ limitations under the License.

NS_ASSUME_NONNULL_BEGIN

/** Error domain of TensorFlow Lite Support related errors. */
static NSString *const TFLSupportTaskErrorDomain = @"org.tensorflow.lite.tasks";

/** Helper utility for the all tasks which encapsulates common functionality. */
@interface TFLCommonUtils : NSObject

Expand All @@ -30,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
* no error will be saved.
*/
+ (void)createCustomError:(NSError **)error
withCode:(NSInteger)code
withCode:(NSUInteger)code
description:(NSString *)description;

/**
Expand All @@ -44,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (void)createCustomError:(NSError **)error
withDomain:(NSString *)domain
code:(NSInteger)code
code:(NSUInteger)code
description:(NSString *)description;

/**
Expand Down
7 changes: 2 additions & 5 deletions tensorflow_lite_support/ios/sources/TFLCommonUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@
#import "tensorflow_lite_support/ios/sources/TFLCommonUtils.h"
#import "tensorflow_lite_support/ios/sources/TFLCommon.h"

/** Error domain of TensorFlow Lite Support related errors. */
static NSString *const TFLSupportTaskErrorDomain = @"org.tensorflow.lite.tasks";

@implementation TFLCommonUtils

+ (void)createCustomError:(NSError **)error
withCode:(NSInteger)code
withCode:(NSUInteger)code
description:(NSString *)description {
[TFLCommonUtils createCustomError:error
withDomain:TFLSupportTaskErrorDomain
Expand All @@ -31,7 +28,7 @@ + (void)createCustomError:(NSError **)error

+ (void)createCustomError:(NSError **)error
withDomain:(NSString *)domain
code:(NSInteger)code
code:(NSUInteger)code
description:(NSString *)description {
if (error) {
*error = [NSError errorWithDomain:domain
Expand Down