The Problem
void function(InnerType const& inputs) {
struct InnerType {
// All the function required parameters
};
}
It's possible to fix the compiler error about the function parameter type?
Possible Solution
struct InnerType;
void function(InnerType const& inputs) {
struct InnerType {
};
}
But it fails with the 'Incomplete Type' error when we try to use it.
Usage
In my recent project, I have to deal with a lot of C APIs including the third-party libraries and the Kernel APIs.
- First I tried to wrap those functions but soon I come up with a lot of duplicate codes to handle trivial things(like checking the API preconditions that some of them have the same code under different name/context),
- Then I tried to use Template Programming to hide the logic of the conditions under templates.
- And that didn't take a long time before I come up with a lot of unreadable templates code only just for a single task(removing the duplication).
So I think that if we could have a Nested-Function-Type, we could easily wrap the API parameter in the C function to avoid a whole new class
with duplicate codes.
My Restrictions
- I can't define the
struct
in the same namespace of the function. Because all of the API handlers are defined in the same file and it's hard to choose unique names. - I can't create a file per handler, because we already have a lot of files.
My Priorities
- Readbility
- Maintainability
- Strong Typing(for dealing with the C APIs)
Example
Expected<ReturnType> GetDataFrom(FunctionParamType const& inputs) {
struct FunctionParamType {
String string;
Integer index;
};
ThrowExceptionOnEmptyString(inputs.string, "Missed Something");
ThrowExceptionOnZeroInteger(inputs.index, "Wrong Index");
// Call The API
}
// Usage
auto const result = GetDataFrom({.string = "something", .index = 1});
Instead of:
class DataApi {
public:
void setString(String const& string) {
ThrowExceptionOnEmptyString(string, "Missed Something");
string_ = string;
}
void setInteger(Integer const& interger) {
ThrowExceptionOnZeroInteger(integer, "Wrong Index");
integer_ = integer;
}
String getString() const noexcept { return string_; }
Integer getInteger() const noexcept { return integer_; }
DataApi(String const& string, Integer const& integer)
: string_(string), integer_(integer) {
ThrowExceptionOnEmptyString(string, "Missed Something");
ThrowExceptionOnZeroInteger(integer, "Wrong Index");
}
DataApi() = default;
Expected<ReturnType> execute() {
// Call The API
}
private:
String string_;
Integer integer_;
};
// Usage
auto const result = DataApi("Something", 1).execute();
CodePudding user response:
How about this approach:
namespace some_api_function {
struct parameter_type {
...
};
struct return_type {
...
};
return_type call(parameter_type p) {
...
}
}
The only name that changes is that of the surrounding namespace. parameter_type
, call
and return_type
remain the same throughout the various API wrappers.
BTW: Your approach with validating setters for the parameter structs is flawed. In particular, you don't guarantee that the parameter is correct if you don't establish that guarantee from the constructor on. Also, if two parameters' correctness depend on each other, that approach doesn't work with separate setters.
Instead, consider writing two more functions validate()
that you call first hand on the parameters and finally on the results. That is also a good way to clearly document these requirements in code.