Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
- Added 200 Bus Synthetic Illinois Case
- Added node objects to `PowerElectronics` module & updated all examples to make use of them.
- Separated internal and external residuals of `PowerElectronics` models.
- Added `CliArgs` class for better management of command-line options.

## v0.1

Expand Down
1 change: 1 addition & 0 deletions GridKit/Utilities/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ target_include_directories(Utilities INTERFACE
add_library(GridKit::Utilities ALIAS Utilities)

add_subdirectory(Logger)
add_subdirectory(CliArgs)
add_subdirectory(CliOptions)

install(TARGETS Utilities EXPORT gridkit-targets)
Expand Down
127 changes: 127 additions & 0 deletions GridKit/Utilities/CliArgs/ArgValue.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/**
* @file ArgValue.hpp
* @author Philip Fackler (facklerpw@ornl.gov)
*/

#pragma once

#include <any>
#include <sstream>
#include <string>

#include <GridKit/Utilities/String.hpp>

namespace GridKit
{
namespace Utilities
{

/**
* @brief Represents a single value of arbitrary type
*
* The value is internally represented as a std::string that is parsed when
* requested as a specific type
*/
class ArgValue
{
/**
* @brief Check that T != ArgValue
*
*/
template <typename T>
static constexpr bool notAnArgValue =
!std::is_same_v<std::remove_reference_t<T>, ArgValue>;

/**
* @brief Check that T != std::initializer_list<ArgValue>
*/
template <typename T>
static constexpr bool notAnArgValueList =
!std::is_same_v<std::remove_reference_t<T>, std::initializer_list<ArgValue>>;

/**
* @brief Check that T is not an ArgValue or list of ArgValues
*/
template <typename T>
static constexpr bool notArgValue = notAnArgValue<T> && notAnArgValueList<T>;

public:
/**
* @brief Default construction results in empty value
*
* @note the SFINAE parameter is used to work around an issue with libc++
*/
ArgValue() = default;

/**
* @brief Copy constructor
*/
ArgValue(const ArgValue&) = default;

/**
* @brief Move constructor
*/
ArgValue(ArgValue&&) = default;

/**
* @brief Construct from any value
*/
template <typename T>
ArgValue(T&& val, std::enable_if_t<notArgValue<T>, int> = 0)
: value_((std::stringstream() << val).str())
{
}

ArgValue& operator=(const ArgValue&) = default;
ArgValue& operator=(ArgValue&&) = default;

/**
* @brief Assign any value
*/
template <typename T>
ArgValue& operator=(T&& val)
{
value_ = (std::stringstream() << val).str();
return *this;
}

/**
* @brief Check if no value is contained
*/
bool empty() const
{
return value_.empty();
}

/**
* @brief Get string representation
*/
const std::string& get() const
{
return value_;
}

/**
* @brief Get string representation
*/
const std::string& operator()() const
{
return get();
}

/**
* @brief Get value of specific expected type
*/
template <typename T>
T as() const
{
return GridKit::Utilities::parse<T>(value_);
}

private:
/// Internal representation
std::string value_{};
};

} // namespace Utilities
} // namespace GridKit
135 changes: 135 additions & 0 deletions GridKit/Utilities/CliArgs/ArgVector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* @file ArgVector.hpp
* @author Philip Fackler (facklerpw@ornl.gov)
*/

#pragma once

#include <array>
#include <string>
#include <vector>

#include <GridKit/Utilities/CliArgs/ArgValue.hpp>

namespace GridKit
{
namespace Utilities
{

// Forward declaration
struct CliArgsImpl;

/**
* @brief Represents a set of 0 or more values associated with a given
* command-line option
*
* This class allows the internal data to be interpreted as a discrete set
* of values (std::array) of a given type or as a single value, hopefully
* making it more intuitive for users. For example...
*
* If an ArgVector v is expected to hold one value of type `double`, that
* value can be extracted with `auto r = v.as<double>();`. This means `r`
* will be a `double` value assigned with the contents of the first element
* of `v`.
*
* If an ArgVector v is expected to hold two values of type `int`, on the
* other hand, the values can be extracted as a `std::array<int, 2>` using
* `as<int, 2>()`. Using `std::array` allows structured bindings for the
* user: `auto [a, b] = v.as<int, 2>();`
*/
class ArgVector
{
public:
/**
* @brief Default is empty (no values)
*/
ArgVector() = default;

/**
* @brief Construct with a single value
*/
template <typename T>
ArgVector(T&& val)
: vec_{val}
{
}

ArgVector(std::initializer_list<ArgValue> vals)
: vec_{vals}
{
}

/**
* @brief Interpret as `N` values of type `T`
*/
template <typename T, std::size_t N>
std::array<T, N> as() const
{
assert(vec_.size() == N);
std::array<T, N> ret;
for (std::size_t i = 0; i < N; ++i)
{
ret[i] = vec_[i].as<T>();
}
return ret;
}

/**
* @brief Interpret as `N` values of type `std::string`
*/
template <std::size_t N>
decltype(auto) as() const
{
return as<std::string, N>();
}

/**
* @brief Interpret as single value of type `T`
*/
template <typename T>
decltype(auto) as() const
{
return vec_[0].as<T>();
}

/**
* @brief Interpret as single `std::string` value
*/
const std::string& operator()() const
{
return vec_[0].get();
}

/**
* @brief Get one of the contained `ArgValue` objects
*/
const ArgValue& operator[](std::size_t i) const
{
return vec_[i];
}

/**
* @brief Check for existence of values
*/
bool empty() const
{
return vec_.empty();
}

/**
* @brief Get number of `ArgValue` objects
*/
std::size_t size() const
{
return vec_.size();
}

private:
/// Internal set of values
std::vector<ArgValue> vec_;

friend struct CliArgsImpl;
};

} // namespace Utilities
} // namespace GridKit
13 changes: 13 additions & 0 deletions GridKit/Utilities/CliArgs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
gridkit_add_library(utilities_cli_args
SOURCES
CliArgs.cpp
HEADERS
CliArgs.hpp
Comment thread
PhilipFackler marked this conversation as resolved.
ArgValue.hpp
ArgVector.hpp
Option.hpp
LINK_LIBRARIES
PUBLIC GridKit::utilities_logger
)

target_link_libraries(Utilities INTERFACE GridKit::utilities_cli_args)
60 changes: 60 additions & 0 deletions GridKit/Utilities/CliArgs/CliArgs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <algorithm>
#include <cassert>
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <vector>

#include <GridKit/Utilities/CliArgs/CliArgs.hpp>
#include <GridKit/Utilities/Logger/Logger.hpp>

#include "CliArgsImpl.hpp"

namespace GridKit
{
namespace Utilities
{
CliArgs::CliArgs(std::initializer_list<Option> opts)
: pImpl_(std::make_unique<CliArgsImpl>(opts))
{
}

CliArgs::~CliArgs()
{
}

std::ostream& operator<<(std::ostream& os, const CliArgs& args)
{
os << *(args.pImpl_);
return os;
}

void CliArgs::parseArgs(int argc, const char* argv[])
{
pImpl_->parseArgs(argc, argv);
}

void CliArgs::printUsage(std::ostream& os) const
{
pImpl_->printUsage(os);
}

void CliArgs::printHelp(std::ostream& os) const
{
pImpl_->printHelp(os);
}

const std::string& CliArgs::getAppName() const
{
return pImpl_->app_name_;
}

const ArgVector& CliArgs::operator[](const std::string& name) const
{
return (*pImpl_)[name];
}

} // namespace Utilities
} // namespace GridKit
Loading
Loading