Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/client/include/ProgramOptions.hpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

613 lines
22 KiB
C++

/*******************************************************************************
*
* MIT License
*
* Copyright (C) 2022-2026 Advanced Micro Devices, Inc. All rights reserved.
*
* Drop-in replacement for boost::program_options used by tensilelite client.
* Uses std::any and standard library only; no Boost dependency.
*
*******************************************************************************/
#pragma once
#include <any>
#include <cctype>
#include <functional>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
namespace TensileLite
{
namespace Client
{
namespace po
{
inline std::vector<std::string> split_string(std::string const& s,
char const* sep = ",;")
{
std::vector<std::string> out;
std::string::size_type pos = 0;
for(;;)
{
auto next = s.find_first_of(sep, pos);
if(next == std::string::npos)
{
if(pos < s.size())
{
std::string part = s.substr(pos);
if(!part.empty())
out.push_back(std::move(part));
}
break;
}
if(next > pos)
out.push_back(s.substr(pos, next - pos));
pos = next + 1;
}
return out;
}
inline bool parse_bool_string(std::string const& s)
{
std::string lower;
lower.reserve(s.size());
for(char c : s)
lower.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
if(lower == "1" || lower == "true" || lower == "yes")
return true;
if(lower == "0" || lower == "false" || lower == "no")
return false;
throw std::runtime_error("Failed to parse bool: " + s);
}
inline std::string trim_opt(std::string s)
{
auto start = s.find_first_not_of(" \t");
if(start == std::string::npos)
return {};
auto end = s.find_last_not_of(" \t");
return s.substr(start, end == std::string::npos ? end : end - start + 1);
}
inline std::string get_canonical_option_name(std::string const& opts)
{
auto pos = opts.find(',');
if(pos == std::string::npos)
return trim_opt(opts);
return trim_opt(opts.substr(0, pos));
}
class variable_value
{
std::any m_value;
public:
variable_value() = default;
bool empty() const
{
return !m_value.has_value();
}
std::any& value()
{
return m_value;
}
std::any const& value() const
{
return m_value;
}
template <typename T>
T const& as() const
{
if(!m_value.has_value())
{
// Match Boost: unset option yields default-constructed T when .as<T>() is used
static T const defaultVal{};
return defaultVal;
}
try
{
return std::any_cast<T const&>(m_value);
}
catch(std::bad_any_cast const&)
{
// Allow bool option stored as string (e.g. "True"/"False" from config)
if constexpr(std::is_same_v<T, bool>)
{
if(m_value.type() == typeid(std::string))
{
static bool converted;
converted
= parse_bool_string(std::any_cast<std::string const&>(m_value));
return converted;
}
}
throw std::runtime_error(
"program_options: bad_any_cast - stored option value has a different "
"type than the requested .as<T>()");
}
}
};
class variables_map
{
std::map<std::string, variable_value> m_map;
static variable_value const s_empty;
public:
using key_type = std::string;
using mapped_type = variable_value;
using value_type = std::pair<const std::string, variable_value>;
using iterator = std::map<std::string, variable_value>::iterator;
using const_iterator = std::map<std::string, variable_value>::const_iterator;
variable_value& operator[](key_type const& k)
{
return m_map[k];
}
variable_value const& operator[](key_type const& k) const
{
auto it = m_map.find(k);
if(it != m_map.end())
return it->second;
return s_empty;
}
variable_value& at(key_type const& k)
{
return m_map.at(k);
}
variable_value const& at(key_type const& k) const
{
return m_map.at(k);
}
size_t count(key_type const& k) const
{
return m_map.count(k);
}
iterator begin()
{
return m_map.begin();
}
iterator end()
{
return m_map.end();
}
const_iterator begin() const
{
return m_map.begin();
}
const_iterator end() const
{
return m_map.end();
}
const_iterator find(key_type const& k) const
{
return m_map.find(k);
}
iterator find(key_type const& k)
{
return m_map.find(k);
}
std::pair<iterator, bool> insert(value_type const& v)
{
return m_map.insert(v);
}
void clear()
{
m_map.clear();
}
};
struct option_value_base
{
virtual ~option_value_base() = default;
virtual std::any get_default() const = 0;
virtual std::function<std::any(std::string const&)> parser() const = 0;
/** Parse one line from config file. Default: parser()(value). Override for Boost-compatible
* behaviour where each config line is one value (no comma split). */
virtual std::any parse_config_line(std::string const& value) const
{
return parser()(value);
}
virtual bool is_multivalued() const
{
return false;
}
virtual bool is_flag() const
{
return false;
}
virtual bool has_explicit_default() const
{
return false;
}
virtual bool has_implicit_value() const
{
return false;
}
virtual std::any get_implicit_value() const
{
return std::any();
}
virtual void merge_into(std::any& current, std::any const& new_val) const
{
current = new_val;
}
};
template <typename T>
struct typed_value_holder : option_value_base
{
T m_default{};
bool m_has_explicit_default = false;
typed_value_holder* default_value(T v)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
typed_value_holder* default_value(T v, std::string const&)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
bool has_explicit_default() const override
{
return m_has_explicit_default;
}
std::any get_default() const override
{
return std::any(m_default);
}
std::function<std::any(std::string const&)> parser() const override
{
return [](std::string const& s) {
T t;
std::istringstream ss(s);
ss >> t;
if(!ss && !ss.eof())
throw std::runtime_error("Failed to parse option value: " + s);
return std::any(t);
};
}
bool is_flag() const override
{
return false;
}
};
template <>
struct typed_value_holder<bool> : option_value_base
{
bool m_default = false;
bool m_has_explicit_default = false;
bool m_has_implicit = false;
bool m_implicit_value = false;
typed_value_holder* default_value(bool v)
{
m_default = v;
m_has_explicit_default = true;
return this;
}
typed_value_holder* default_value(bool v, std::string const&)
{
m_default = v;
m_has_explicit_default = true;
return this;
}
typed_value_holder* implicit_value(bool v)
{
m_has_implicit = true;
m_implicit_value = v;
return this;
}
bool has_explicit_default() const override
{
return m_has_explicit_default;
}
bool has_implicit_value() const override
{
return m_has_implicit;
}
std::any get_implicit_value() const override
{
return std::any(m_implicit_value);
}
std::any get_default() const override
{
return std::any(m_default);
}
std::function<std::any(std::string const&)> parser() const override
{
return [](std::string const& s) { return std::any(parse_bool_string(s)); };
}
bool is_flag() const override
{
return true;
}
};
template <typename U>
struct typed_value_holder<std::vector<U>> : option_value_base
{
std::vector<U> m_default;
bool m_has_explicit_default = false;
typed_value_holder* default_value(std::vector<U> v)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
typed_value_holder* default_value(std::vector<U> v, std::string const&)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
bool has_explicit_default() const override
{
return m_has_explicit_default;
}
std::any get_default() const override
{
return std::any(m_default);
}
bool is_multivalued() const override
{
return true;
}
std::function<std::any(std::string const&)> parser() const override
{
return [](std::string const& s) {
auto parts = split_string(s);
std::vector<U> out;
out.reserve(parts.size());
for(auto const& p : parts)
{
if(p.empty())
continue;
U u;
std::istringstream ss(p);
ss >> u;
if(!ss && !ss.eof())
throw std::runtime_error("Failed to parse: " + p);
out.push_back(std::move(u));
}
return std::any(out);
};
}
void merge_into(std::any& current, std::any const& new_val) const override
{
auto& c = std::any_cast<std::vector<U>&>(current);
auto const& n = std::any_cast<std::vector<U> const&>(new_val);
c.insert(c.end(), n.begin(), n.end());
}
};
template <>
struct typed_value_holder<std::vector<std::string>> : option_value_base
{
std::vector<std::string> m_default;
bool m_has_explicit_default = false;
typed_value_holder* default_value(std::vector<std::string> v)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
typed_value_holder* default_value(std::vector<std::string> v, std::string const&)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
bool has_explicit_default() const override
{
return m_has_explicit_default;
}
std::any get_default() const override
{
return std::any(m_default);
}
bool is_multivalued() const override
{
return true;
}
std::function<std::any(std::string const&)> parser() const override
{
return [](std::string const& s) { return std::any(split_string(s)); };
}
/** Boost config file behaviour: each line is one value (no comma split). */
std::any parse_config_line(std::string const& value) const override
{
std::string t = trim_opt(value);
if(t.empty())
return std::any(std::vector<std::string>{});
return std::any(std::vector<std::string>{std::move(t)});
}
void merge_into(std::any& current, std::any const& new_val) const override
{
auto& c = std::any_cast<std::vector<std::string>&>(current);
auto const& n = std::any_cast<std::vector<std::string> const&>(new_val);
c.insert(c.end(), n.begin(), n.end());
}
};
template <>
struct typed_value_holder<std::vector<bool>> : option_value_base
{
std::vector<bool> m_default;
bool m_has_explicit_default = false;
typed_value_holder* default_value(std::vector<bool> v)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
typed_value_holder* default_value(std::vector<bool> v, std::string const&)
{
m_default = std::move(v);
m_has_explicit_default = true;
return this;
}
bool has_explicit_default() const override
{
return m_has_explicit_default;
}
std::any get_default() const override
{
return std::any(m_default);
}
bool is_multivalued() const override
{
return true;
}
std::function<std::any(std::string const&)> parser() const override
{
return [](std::string const& s) {
auto parts = split_string(s);
std::vector<bool> out;
out.reserve(parts.size());
for(auto const& p : parts)
{
if(p.empty())
continue;
out.push_back(parse_bool_string(p));
}
return std::any(out);
};
}
void merge_into(std::any& current, std::any const& new_val) const override
{
auto& c = std::any_cast<std::vector<bool>&>(current);
auto const& n = std::any_cast<std::vector<bool> const&>(new_val);
c.insert(c.end(), n.begin(), n.end());
}
};
template <typename T>
using typed_value = typed_value_holder<T>;
template <typename T>
typed_value_holder<T>* value()
{
return new typed_value_holder<T>();
}
class options_description
{
public:
struct option_entry
{
std::string opts; // e.g. "help,h"
std::string description;
std::unique_ptr<option_value_base> value_semantic;
};
using option_list = std::vector<option_entry>;
explicit options_description(std::string const&) {}
class add_options_helper
{
option_list& m_list;
public:
explicit add_options_helper(option_list& list)
: m_list(list)
{
}
add_options_helper& operator()(std::string const& opts, std::string const& desc)
{
auto* e = new typed_value_holder<bool>();
e->default_value(false);
m_list.push_back({opts, desc, std::unique_ptr<option_value_base>(e)});
return *this;
}
template <typename T>
add_options_helper& operator()(std::string const& opts,
typed_value_holder<T>* val,
std::string const& desc)
{
m_list.push_back({opts, desc, std::unique_ptr<option_value_base>(val)});
return *this;
}
};
add_options_helper add_options()
{
return add_options_helper(m_options);
}
option_list const& options() const
{
return m_options;
}
private:
option_list m_options;
};
variables_map parse_command_line(int argc,
char const* const argv[],
options_description const& desc);
variables_map parse_config_file(std::istream& is, options_description const& desc);
inline void store(variables_map const& from, variables_map& to)
{
for(auto const& kv : from)
to[kv.first].value() = kv.second.value();
}
inline void notify(variables_map const&) {}
std::ostream& operator<<(std::ostream& os, options_description const& desc);
} // namespace po
} // namespace Client
} // namespace TensileLite