/* * Copyright (c) 2024 Project CHIP 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. * */ #include "Command.h" #include "CustomStringPrefix.h" #include "HexConversion.h" #include "platform/PlatformManager.h" #include #include #include #include #include #include #include // For INFINITY #include #include #include #include #include #include #include #include constexpr char kOptionalArgumentPrefix[] = "--"; constexpr size_t kOptionalArgumentPrefixLength = 2; bool Command::InitArguments(int argc, char ** argv) { bool isValidCommand = false; size_t argvExtraArgsCount = (size_t) argc; size_t mandatoryArgsCount = 0; size_t optionalArgsCount = 0; for (auto & arg : mArgs) { if (arg.isOptional()) { optionalArgsCount++; } else { mandatoryArgsCount++; argvExtraArgsCount--; } } VerifyOrExit((size_t) (argc) >= mandatoryArgsCount && (argvExtraArgsCount == 0 || (argvExtraArgsCount && optionalArgsCount)), ChipLogError(NotSpecified, "InitArgs: Wrong arguments number: %d instead of %u", argc, static_cast(mandatoryArgsCount))); // Initialize mandatory arguments for (size_t i = 0; i < mandatoryArgsCount; i++) { char * arg = argv[i]; if (!InitArgument(i, arg)) { ExitNow(); } } // Initialize optional arguments // Optional arguments expect a name and a value, so i is increased by 2 on every step. for (size_t i = mandatoryArgsCount; i < (size_t) argc; i += 2) { bool found = false; for (size_t j = mandatoryArgsCount; j < mandatoryArgsCount + optionalArgsCount; j++) { // optional arguments starts with kOptionalArgumentPrefix if (strlen(argv[i]) <= kOptionalArgumentPrefixLength && strncmp(argv[i], kOptionalArgumentPrefix, kOptionalArgumentPrefixLength) != 0) { continue; } if (strcmp(argv[i] + strlen(kOptionalArgumentPrefix), mArgs[j].name) == 0) { found = true; VerifyOrExit((size_t) argc > (i + 1), ChipLogError(NotSpecified, "InitArgs: Optional argument %s missing value.", argv[i])); if (!InitArgument(j, argv[i + 1])) { ExitNow(); } } } VerifyOrExit(found, ChipLogError(NotSpecified, "InitArgs: Optional argument %s does not exist.", argv[i])); } isValidCommand = true; exit: return isValidCommand; } static bool ParseAddressWithInterface(const char * addressString, Command::AddressWithInterface * address) { struct addrinfo hints; struct addrinfo * result; int ret; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; ret = getaddrinfo(addressString, nullptr, &hints, &result); if (ret < 0) { ChipLogError(NotSpecified, "Invalid address: %s", addressString); return false; } if (result->ai_family == AF_INET6) { struct sockaddr_in6 * addr = reinterpret_cast(result->ai_addr); address->address = ::chip::Inet::IPAddress::FromSockAddr(*addr); address->interfaceId = ::chip::Inet::InterfaceId(addr->sin6_scope_id); } #if INET_CONFIG_ENABLE_IPV4 else if (result->ai_family == AF_INET) { address->address = ::chip::Inet::IPAddress::FromSockAddr(*reinterpret_cast(result->ai_addr)); address->interfaceId = chip::Inet::InterfaceId::Null(); } #endif // INET_CONFIG_ENABLE_IPV4 else { ChipLogError(NotSpecified, "Unsupported address: %s", addressString); return false; } return true; } // The callback should return whether the argument is valid, for the non-null // case. It can't directly write to isValidArgument (by closing over it) // because in the nullable-and-null case we need to do that from this function, // via the return value. template bool HandleNullableOptional(Argument & arg, char * argValue, std::function callback) { if (arg.isOptional()) { if (arg.isNullable()) { arg.value = &(reinterpret_cast> *>(arg.value)->Emplace()); } else { arg.value = &(reinterpret_cast *>(arg.value)->Emplace()); } } if (arg.isNullable()) { auto * nullable = reinterpret_cast *>(arg.value); if (strcmp(argValue, "null") == 0) { nullable->SetNull(); return true; } arg.value = &(nullable->SetNonNull()); } return callback(reinterpret_cast(arg.value)); } bool Command::InitArgument(size_t argIndex, char * argValue) { bool isValidArgument = false; bool isHexNotation = strncmp(argValue, "0x", 2) == 0 || strncmp(argValue, "0X", 2) == 0; Argument arg = mArgs.at(argIndex); // We have two places where we handle uint8_t-typed args (actual int8u and // bool args), so declare the handler function here so it can be reused. auto uint8Handler = [&](uint8_t * value) { // stringstream treats uint8_t as char, which is not what we want here. uint16_t tmpValue; std::stringstream ss; isHexNotation ? (ss << std::hex << argValue) : (ss << argValue); ss >> tmpValue; if (chip::CanCastTo(tmpValue)) { *value = static_cast(tmpValue); uint64_t min = chip::CanCastTo(arg.min) ? static_cast(arg.min) : 0; uint64_t max = arg.max; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); } return false; }; switch (arg.type) { case ArgumentType::Complex: { // Complex arguments may be optional, but they are not currently supported via the class. // Instead, they must be explicitly specified as optional using the kOptional flag, // and the base TypedComplexArgument class is still referenced. auto complexArgument = static_cast(arg.value); return CHIP_NO_ERROR == complexArgument->Parse(arg.name, argValue); } case ArgumentType::Custom: { auto customArgument = static_cast(arg.value); return CHIP_NO_ERROR == customArgument->Parse(arg.name, argValue); } case ArgumentType::VectorString: { std::vector vectorArgument; chip::StringSplitter splitter(argValue, ','); chip::CharSpan value; while (splitter.Next(value)) { vectorArgument.push_back(std::string(value.data(), value.size())); } if (arg.flags == Argument::kOptional) { auto argument = static_cast> *>(arg.value); argument->SetValue(vectorArgument); } else { auto argument = static_cast *>(arg.value); *argument = vectorArgument; } return true; } case ArgumentType::VectorBool: { // Currently only chip::Optional> is supported. if (arg.flags != Argument::kOptional) { return false; } std::vector vectorArgument; std::stringstream ss(argValue); while (ss.good()) { std::string valueAsString; getline(ss, valueAsString, ','); if (strcasecmp(valueAsString.c_str(), "true") == 0) { vectorArgument.push_back(true); } else if (strcasecmp(valueAsString.c_str(), "false") == 0) { vectorArgument.push_back(false); } else { return false; } } auto optionalArgument = static_cast> *>(arg.value); optionalArgument->SetValue(vectorArgument); return true; } case ArgumentType::Vector16: case ArgumentType::Vector32: { std::vector values; uint64_t min = chip::CanCastTo(arg.min) ? static_cast(arg.min) : 0; uint64_t max = arg.max; std::stringstream ss(argValue); while (ss.good()) { std::string valueAsString; getline(ss, valueAsString, ','); isHexNotation = strncmp(valueAsString.c_str(), "0x", 2) == 0 || strncmp(valueAsString.c_str(), "0X", 2) == 0; std::stringstream subss; isHexNotation ? subss << std::hex << valueAsString : subss << valueAsString; uint64_t value; subss >> value; VerifyOrReturnError(!subss.fail() && subss.eof() && value >= min && value <= max, false); values.push_back(value); } if (arg.type == ArgumentType::Vector16) { auto vectorArgument = static_cast *>(arg.value); for (uint64_t v : values) { vectorArgument->push_back(static_cast(v)); } } else if (arg.type == ArgumentType::Vector32 && arg.flags != Argument::kOptional) { auto vectorArgument = static_cast *>(arg.value); for (uint64_t v : values) { vectorArgument->push_back(static_cast(v)); } } else if (arg.type == ArgumentType::Vector32 && arg.flags == Argument::kOptional) { std::vector vectorArgument; for (uint64_t v : values) { vectorArgument.push_back(static_cast(v)); } auto optionalArgument = static_cast> *>(arg.value); optionalArgument->SetValue(vectorArgument); } else { return false; } return true; } case ArgumentType::VectorCustom: { auto vectorArgument = static_cast *>(arg.value); std::stringstream ss(argValue); while (ss.good()) { std::string valueAsString; // By default the parameter separator is ";" in order to not collapse with the argument itself if it contains commas // (e.g a struct argument with multiple fields). In case one needs to use ";" it can be overriden with the following // environment variable. static constexpr char kSeparatorVariable[] = "NotSpecified,_CUSTOM_ARGUMENTS_SEPARATOR"; char * getenvSeparatorVariableResult = getenv(kSeparatorVariable); getline(ss, valueAsString, getenvSeparatorVariableResult ? getenvSeparatorVariableResult[0] : ';'); CustomArgument * customArgument = new CustomArgument(); vectorArgument->push_back(customArgument); VerifyOrReturnError(CHIP_NO_ERROR == vectorArgument->back()->Parse(arg.name, valueAsString.c_str()), false); } return true; } case ArgumentType::String: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { *value = argValue; return true; }); break; } case ArgumentType::CharString: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { *value = chip::Span(argValue, strlen(argValue)); return true; }); break; } case ArgumentType::OctetString: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { // We support two ways to pass an octet string argument. If it happens // to be all-ASCII, you can just pass it in. Otherwise you can pass in // "hex:" followed by the hex-encoded bytes. size_t argLen = strlen(argValue); if (IsHexString(argValue)) { // Hex-encoded. Decode it into a temporary buffer first, so if we // run into errors we can do correct "argument is not valid" logging // that actually shows the value that was passed in. After we // determine it's valid, modify the passed-in value to hold the // right bytes, so we don't need to worry about allocating storage // for this somewhere else. This works because the hex // representation is always longer than the octet string it encodes, // so we have enough space in argValue for the decoded version. chip::Platform::ScopedMemoryBuffer buffer; size_t octetCount; CHIP_ERROR err = HexToBytes( chip::CharSpan(argValue + kHexStringPrefixLen, argLen - kHexStringPrefixLen), [&buffer](size_t allocSize) { buffer.Calloc(allocSize); return buffer.Get(); }, &octetCount); if (err != CHIP_NO_ERROR) { return false; } memcpy(argValue, buffer.Get(), octetCount); *value = chip::ByteSpan(chip::Uint8::from_char(argValue), octetCount); return true; } // Just ASCII. Check for the "str:" prefix. if (IsStrString(argValue)) { // Skip the prefix argValue += kStrStringPrefixLen; argLen -= kStrStringPrefixLen; } *value = chip::ByteSpan(chip::Uint8::from_char(argValue), argLen); return true; }); break; } case ArgumentType::Bool: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { // Start with checking for actual boolean values. if (strcasecmp(argValue, "true") == 0) { *value = true; return true; } if (strcasecmp(argValue, "false") == 0) { *value = false; return true; } // For backwards compat, keep accepting 0 and 1 for now as synonyms // for false and true. Since we set our min to 0 and max to 1 for // booleans, calling uint8Handler does the right thing in terms of // only allowing those two values. uint8_t temp = 0; if (!uint8Handler(&temp)) { return false; } *value = (temp == 1); return true; }); break; } case ArgumentType::Number_uint8: { isValidArgument = HandleNullableOptional(arg, argValue, uint8Handler); break; } case ArgumentType::Number_uint16: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { std::stringstream ss; isHexNotation ? ss << std::hex << argValue : ss << argValue; ss >> *value; uint64_t min = chip::CanCastTo(arg.min) ? static_cast(arg.min) : 0; uint64_t max = arg.max; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); }); break; } case ArgumentType::Number_uint32: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { std::stringstream ss; isHexNotation ? ss << std::hex << argValue : ss << argValue; ss >> *value; uint64_t min = chip::CanCastTo(arg.min) ? static_cast(arg.min) : 0; uint64_t max = arg.max; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); }); break; } case ArgumentType::Number_uint64: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { std::stringstream ss; isHexNotation ? ss << std::hex << argValue : ss << argValue; ss >> *value; uint64_t min = chip::CanCastTo(arg.min) ? static_cast(arg.min) : 0; uint64_t max = arg.max; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); }); break; } case ArgumentType::Number_int8: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { // stringstream treats int8_t as char, which is not what we want here. int16_t tmpValue; std::stringstream ss; isHexNotation ? ss << std::hex << argValue : ss << argValue; ss >> tmpValue; if (chip::CanCastTo(tmpValue)) { *value = static_cast(tmpValue); int64_t min = arg.min; int64_t max = chip::CanCastTo(arg.max) ? static_cast(arg.max) : INT64_MAX; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); } return false; }); break; } case ArgumentType::Number_int16: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { std::stringstream ss; isHexNotation ? ss << std::hex << argValue : ss << argValue; ss >> *value; int64_t min = arg.min; int64_t max = chip::CanCastTo(arg.max) ? static_cast(arg.max) : INT64_MAX; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); }); break; } case ArgumentType::Number_int32: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { std::stringstream ss; isHexNotation ? ss << std::hex << argValue : ss << argValue; ss >> *value; int64_t min = arg.min; int64_t max = chip::CanCastTo(arg.max) ? static_cast(arg.max) : INT64_MAX; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); }); break; } case ArgumentType::Number_int64: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { std::stringstream ss; isHexNotation ? ss << std::hex << argValue : ss << argValue; ss >> *value; int64_t min = arg.min; int64_t max = chip::CanCastTo(arg.max) ? static_cast(arg.max) : INT64_MAX; return (!ss.fail() && ss.eof() && *value >= min && *value <= max); }); break; } case ArgumentType::Float: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { if (strcmp(argValue, "Infinity") == 0) { *value = INFINITY; return true; } if (strcmp(argValue, "-Infinity") == 0) { *value = -INFINITY; return true; } std::stringstream ss; ss << argValue; ss >> *value; return (!ss.fail() && ss.eof()); }); break; } case ArgumentType::Double: { isValidArgument = HandleNullableOptional(arg, argValue, [&](auto * value) { if (strcmp(argValue, "Infinity") == 0) { *value = INFINITY; return true; } if (strcmp(argValue, "-Infinity") == 0) { *value = -INFINITY; return true; } std::stringstream ss; ss << argValue; ss >> *value; return (!ss.fail() && ss.eof()); }); break; } case ArgumentType::Address: { isValidArgument = HandleNullableOptional( arg, argValue, [&](auto * value) { return ParseAddressWithInterface(argValue, value); }); break; } } if (!isValidArgument) { ChipLogError(NotSpecified, "InitArgs: Invalid argument %s: %s", arg.name, argValue); } return isValidArgument; } void Command::AddArgument(const char * name, const char * value, const char * desc) { ReadOnlyGlobalCommandArgument arg; arg.name = name; arg.value = value; arg.desc = desc; mReadOnlyGlobalCommandArgument.SetValue(arg); } size_t Command::AddArgument(const char * name, char ** value, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::String; arg.name = name; arg.value = reinterpret_cast(value); arg.flags = flags; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, chip::CharSpan * value, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::CharString; arg.name = name; arg.value = reinterpret_cast(value); arg.flags = flags; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, chip::ByteSpan * value, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::OctetString; arg.name = name; arg.value = reinterpret_cast(value); arg.flags = flags; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, AddressWithInterface * out, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::Address; arg.name = name; arg.value = reinterpret_cast(out); arg.flags = flags; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, std::vector * value, const char * desc) { Argument arg; arg.type = ArgumentType::Vector16; arg.name = name; arg.value = static_cast(value); arg.min = min; arg.max = max; arg.flags = 0; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, std::vector * value, const char * desc) { Argument arg; arg.type = ArgumentType::Vector32; arg.name = name; arg.value = static_cast(value); arg.min = min; arg.max = max; arg.flags = 0; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, chip::Optional> * value, const char * desc) { Argument arg; arg.type = ArgumentType::Vector32; arg.name = name; arg.value = static_cast(value); arg.min = min; arg.max = max; arg.flags = Argument::kOptional; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, chip::Optional> * value, const char * desc) { Argument arg; arg.type = ArgumentType::VectorBool; arg.name = name; arg.value = static_cast(value); arg.min = min; arg.max = max; arg.flags = Argument::kOptional; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, ComplexArgument * value, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::Complex; arg.name = name; arg.value = static_cast(value); arg.flags = flags; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, CustomArgument * value, const char * desc) { Argument arg; arg.type = ArgumentType::Custom; arg.name = name; arg.value = const_cast(reinterpret_cast(value)); arg.flags = 0; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, std::vector * value, const char * desc) { Argument arg; arg.type = ArgumentType::VectorCustom; arg.name = name; arg.value = static_cast(value); arg.flags = 0; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, float min, float max, float * out, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::Float; arg.name = name; arg.value = reinterpret_cast(out); arg.flags = flags; arg.desc = desc; // Ignore min/max for now; they're always +-Infinity anyway. return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, double min, double max, double * out, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::Double; arg.name = name; arg.value = reinterpret_cast(out); arg.flags = flags; arg.desc = desc; // Ignore min/max for now; they're always +-Infinity anyway. return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, void * out, ArgumentType type, const char * desc, uint8_t flags) { Argument arg; arg.type = type; arg.name = name; arg.value = out; arg.min = min; arg.max = max; arg.flags = flags; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, int64_t min, uint64_t max, void * out, const char * desc, uint8_t flags) { Argument arg; arg.type = ArgumentType::Number_uint8; arg.name = name; arg.value = out; arg.min = min; arg.max = max; arg.flags = flags; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, std::vector * value, const char * desc) { Argument arg; arg.type = ArgumentType::VectorString; arg.name = name; arg.value = static_cast(value); arg.flags = 0; arg.desc = desc; return AddArgumentToList(std::move(arg)); } size_t Command::AddArgument(const char * name, chip::Optional> * value, const char * desc) { Argument arg; arg.type = ArgumentType::VectorString; arg.name = name; arg.value = static_cast(value); arg.flags = Argument::kOptional; arg.desc = desc; return AddArgumentToList(std::move(arg)); } const char * Command::GetArgumentName(size_t index) const { if (index < mArgs.size()) { return mArgs.at(index).name; } return nullptr; } const char * Command::GetArgumentDescription(size_t index) const { if (index < mArgs.size()) { return mArgs.at(index).desc; } return nullptr; } const char * Command::GetReadOnlyGlobalCommandArgument() const { if (GetAttribute()) { return GetAttribute(); } if (GetEvent()) { return GetEvent(); } return nullptr; } const char * Command::GetAttribute() const { if (mReadOnlyGlobalCommandArgument.HasValue()) { return mReadOnlyGlobalCommandArgument.Value().value; } return nullptr; } const char * Command::GetEvent() const { if (mReadOnlyGlobalCommandArgument.HasValue()) { return mReadOnlyGlobalCommandArgument.Value().value; } return nullptr; } size_t Command::AddArgumentToList(Argument && argument) { if (argument.isOptional() || mArgs.empty() || !mArgs.back().isOptional()) { // Safe to just append. mArgs.emplace_back(std::move(argument)); return mArgs.size(); } // We're inserting a non-optional arg but we already have something optional // in the list. Insert before the first optional arg. for (auto cur = mArgs.cbegin(), end = mArgs.cend(); cur != end; ++cur) { if ((*cur).isOptional()) { mArgs.emplace(cur, std::move(argument)); return mArgs.size(); } } // Never reached. VerifyOrDie(false); return 0; } namespace { template void ResetOptionalArg(const Argument & arg) { VerifyOrDie(arg.isOptional()); if (arg.isNullable()) { reinterpret_cast> *>(arg.value)->ClearValue(); } else { reinterpret_cast *>(arg.value)->ClearValue(); } } } // anonymous namespace void Command::ResetArguments() { for (const auto & arg : mArgs) { const ArgumentType type = arg.type; if (arg.isOptional()) { // Must always clean these up so they don't carry over to the next // command invocation in interactive mode. switch (type) { case ArgumentType::Complex: { // Optional Complex arguments are not currently supported via the class. // Instead, they must be explicitly specified as optional using the kOptional flag, // and the base TypedComplexArgument class is referenced. auto argument = static_cast(arg.value); argument->Reset(); break; } case ArgumentType::Custom: { // No optional custom arguments so far. VerifyOrDie(false); break; } case ArgumentType::VectorString: { ResetOptionalArg>(arg); break; } case ArgumentType::VectorBool: { ResetOptionalArg>(arg); break; } case ArgumentType::Vector16: { // No optional Vector16 arguments so far. VerifyOrDie(false); break; } case ArgumentType::Vector32: { ResetOptionalArg>(arg); break; } case ArgumentType::VectorCustom: { // No optional VectorCustom arguments so far. VerifyOrDie(false); break; } case ArgumentType::String: { ResetOptionalArg(arg); break; } case ArgumentType::CharString: { ResetOptionalArg(arg); break; } case ArgumentType::OctetString: { ResetOptionalArg(arg); break; } case ArgumentType::Bool: { ResetOptionalArg(arg); break; } case ArgumentType::Number_uint8: { ResetOptionalArg(arg); break; } case ArgumentType::Number_uint16: { ResetOptionalArg(arg); break; } case ArgumentType::Number_uint32: { ResetOptionalArg(arg); break; } case ArgumentType::Number_uint64: { ResetOptionalArg(arg); break; } case ArgumentType::Number_int8: { ResetOptionalArg(arg); break; } case ArgumentType::Number_int16: { ResetOptionalArg(arg); break; } case ArgumentType::Number_int32: { ResetOptionalArg(arg); break; } case ArgumentType::Number_int64: { ResetOptionalArg(arg); break; } case ArgumentType::Float: { ResetOptionalArg(arg); break; } case ArgumentType::Double: { ResetOptionalArg(arg); break; } case ArgumentType::Address: { ResetOptionalArg(arg); break; } } } else { // Some non-optional arguments have state that needs to be cleaned // up too. if (type == ArgumentType::Vector16) { auto vectorArgument = static_cast *>(arg.value); vectorArgument->clear(); } else if (type == ArgumentType::Vector32) { auto vectorArgument = static_cast *>(arg.value); vectorArgument->clear(); } else if (type == ArgumentType::VectorCustom) { auto vectorArgument = static_cast *>(arg.value); for (auto & customArgument : *vectorArgument) { delete customArgument; } vectorArgument->clear(); } else if (type == ArgumentType::Complex) { auto argument = static_cast(arg.value); argument->Reset(); } } } }