Files
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

388 lines
12 KiB
C++

/* ************************************************************************
* Copyright (C) 2020-2021 Advanced Micro Devices, Inc. All rights Reserved.
* ************************************************************************ */
// This emulates the required functionality of boost::program_options
#pragma once
#include <cinttypes>
#include <cstdio>
#include <iomanip>
#include <ostream>
#include <regex>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
// Regular expression for token delimiters (whitespace and commas)
static const std::regex program_options_regex{"[, \\f\\n\\r\\t\\v]+",
std::regex_constants::optimize};
// variables_map is a set of seen options
using variables_map = std::set<std::string>;
// Polymorphic base class to use with dynamic_cast
class value_base
{
protected:
bool m_has_default = false;
public:
bool has_default() const
{
return m_has_default;
}
virtual ~value_base() = default;
};
// Value parameters
template <typename T>
class value : public value_base
{
T* m_var; // Pointer to variable to be modified
public:
// Constructor
explicit value(T* var)
: m_var(var)
{
}
// Pointer to variable
T* get_ptr() const
{
return m_var;
}
// Allows default_value()
value* operator->()
{
return this;
}
// Set default value
value& default_value(T val)
{
*m_var = std::move(val);
m_has_default = true;
return *this;
}
};
// bool_switch is a value<bool>, which is handled specially
using bool_switch = value<bool>;
class options_description
{
// desc_option describes a particular option
class desc_option
{
std::string m_opts;
value_base* m_val;
std::string m_desc;
public:
desc_option& operator=(const desc_option&) = delete;
// Constructor with options, value and description
template <typename T>
desc_option(std::string opts, value<T> val, std::string desc)
: m_opts(std::move(opts))
, m_val(new auto(std::move(val)))
, m_desc(std::move(desc))
{
}
// Constructor with options and description
desc_option(std::string opts, std::string desc)
: m_opts(std::move(opts))
, m_val(nullptr)
, m_desc(std::move(desc))
{
}
// Copy constructor is deleted
desc_option(const desc_option&) = delete;
// Move constructor
desc_option(desc_option&& other)
: m_opts(std::move(other.m_opts))
, m_val(other.m_val)
, m_desc(std::move(other.m_desc))
{
other.m_val = nullptr;
}
// Destructor
~desc_option()
{
delete m_val;
}
// Accessors
const std::string& get_opts() const
{
return m_opts;
}
const value_base* get_val() const
{
return m_val;
}
const std::string& get_desc() const
{
return m_desc;
}
// Set a value
void set_val(int& argc, char**& argv) const
{
// We test all supported types with dynamic_cast and parse accordingly
bool match = false;
if(dynamic_cast<value<int32_t>*>(m_val))
{
auto* val = dynamic_cast<value<int32_t>*>(m_val)->get_ptr();
match = argc && sscanf(*argv, "%" SCNd32, val) == 1;
}
else if(dynamic_cast<value<uint32_t>*>(m_val))
{
auto* val = dynamic_cast<value<uint32_t>*>(m_val)->get_ptr();
match = argc && sscanf(*argv, "%" SCNu32, val) == 1;
}
else if(dynamic_cast<value<int64_t>*>(m_val))
{
auto* val = dynamic_cast<value<int64_t>*>(m_val)->get_ptr();
match = argc && sscanf(*argv, "%" SCNd64, val) == 1;
}
else if(dynamic_cast<value<uint64_t>*>(m_val))
{
auto* val = dynamic_cast<value<uint64_t>*>(m_val)->get_ptr();
match = argc && sscanf(*argv, "%" SCNu64, val) == 1;
}
else if(dynamic_cast<value<float>*>(m_val))
{
auto* val = dynamic_cast<value<float>*>(m_val)->get_ptr();
match = argc && sscanf(*argv, "%f", val) == 1;
}
else if(dynamic_cast<value<double>*>(m_val))
{
auto* val = dynamic_cast<value<double>*>(m_val)->get_ptr();
match = argc && sscanf(*argv, "%lf", val) == 1;
}
else if(dynamic_cast<value<char>*>(m_val))
{
auto* val = dynamic_cast<value<char>*>(m_val)->get_ptr();
match = argc && sscanf(*argv, " %c", val) == 1;
}
else if(dynamic_cast<value<bool>*>(m_val))
{
// We handle bool specially, setting the value to true without argument
auto* val = dynamic_cast<value<bool>*>(m_val)->get_ptr();
*val = true;
return;
}
else if(dynamic_cast<value<std::string>*>(m_val))
{
if(argc)
{
*dynamic_cast<value<std::string>*>(m_val)->get_ptr() = *argv;
match = true;
}
}
else
{
throw std::logic_error("Internal error: Unsupported data type");
}
if(!match)
throw std::invalid_argument(argc ? *argv : "Missing required argument");
// Skip past the argument's value
++argv;
--argc;
}
};
// Description and option list
std::string m_desc;
std::vector<desc_option> m_optlist;
// desc_optionlist allows chains of options to be parenthesized
class desc_optionlist
{
std::vector<desc_option>& m_list;
public:
explicit desc_optionlist(std::vector<desc_option>& list)
: m_list(list)
{
}
template <typename... Ts>
desc_optionlist operator()(Ts&&... arg)
{
m_list.push_back(desc_option(std::forward<Ts>(arg)...));
return *this;
}
};
public:
// Constructor
explicit options_description(std::string desc)
: m_desc(std::move(desc))
{
}
// Start a desc_optionlist chain
desc_optionlist add_options() &
{
return desc_optionlist(m_optlist);
}
// Parse an option at the current (argc, argv) position
void parse_option(int& argc, char**& argv, variables_map& vm, bool ignoreUnknown = false) const
{
// Iterate across all options
for(const auto& opt : m_optlist)
{
// Canonical name used for map
std::string canonical_name;
// Iterate across tokens in the opts
for(std::sregex_token_iterator tok{
opt.get_opts().begin(), opt.get_opts().end(), program_options_regex, -1};
tok != std::sregex_token_iterator();
++tok)
{
// The first option in a list of options is the canonical name
if(!canonical_name.length())
canonical_name = tok->str();
// If the length of the option is 1, it is single-dash; otherwise double-dash
const char* prefix = tok->length() == 1 ? "-" : "--";
// If option matches
if(*argv == prefix + tok->str())
{
++argv;
--argc;
// If option has a value, set it; otherwise indicate option in set
if(opt.get_val())
opt.set_val(argc, argv);
else
vm.insert(canonical_name);
return; // Return successfully
}
}
}
// No options were matched
if(ignoreUnknown)
{
++argv;
--argc;
}
else
{
throw std::invalid_argument(*argv);
}
}
// Formatted output of command-line arguments description
friend std::ostream& operator<<(std::ostream& os, const options_description& d)
{
// Iterate across all options
for(const auto& opt : d.m_optlist)
{
bool first = true;
const char* delim = "";
std::ostringstream left;
// Iterate across tokens in the opts
for(std::sregex_token_iterator tok{opt.get_opts().begin(),
opt.get_opts().end(),
program_options_regex,
-1};
tok != std::sregex_token_iterator();
++tok, first = false, delim = " ")
{
// If the length of the option is 1, it is single-dash; otherwise double-dash
const char* prefix = tok->length() == 1 ? "-" : "--";
left << delim << (first ? "" : "[ ") << prefix << tok->str() << (first ? "" : " ]");
}
// Print the default value of the variable type if it exists
// We do not print the default value for bool
const value_base* val = opt.get_val();
if(val && !dynamic_cast<const value<bool>*>(val))
{
left << " arg";
if(val->has_default())
{
// We test all supported types with dynamic_cast and print accordingly
left << " (=";
if(dynamic_cast<const value<int32_t>*>(val))
left << *dynamic_cast<const value<int32_t>*>(val)->get_ptr();
else if(dynamic_cast<const value<uint32_t>*>(val))
left << *dynamic_cast<const value<uint32_t>*>(val)->get_ptr();
else if(dynamic_cast<const value<int64_t>*>(val))
left << *dynamic_cast<const value<int64_t>*>(val)->get_ptr();
else if(dynamic_cast<const value<uint64_t>*>(val))
left << *dynamic_cast<const value<uint64_t>*>(val)->get_ptr();
else if(dynamic_cast<const value<float>*>(val))
left << *dynamic_cast<const value<float>*>(val)->get_ptr();
else if(dynamic_cast<const value<double>*>(val))
left << *dynamic_cast<const value<double>*>(val)->get_ptr();
else if(dynamic_cast<const value<char>*>(val))
left << *dynamic_cast<const value<char>*>(val)->get_ptr();
else if(dynamic_cast<const value<std::string>*>(val))
left << *dynamic_cast<const value<std::string>*>(val)->get_ptr();
else
throw std::logic_error("Internal error: Unsupported data type");
left << ")";
}
}
os << std::setw(36) << std::left << left.str() << " " << opt.get_desc() << "\n\n";
}
return os << std::flush;
}
};
// Class representing command line parser
class parse_command_line
{
variables_map m_vm;
public:
parse_command_line(int argc,
char** argv,
const options_description& desc,
bool ignoreUnknown = false)
{
++argv; // Skip argv[0]
--argc;
while(argc)
desc.parse_option(argc, argv, m_vm, ignoreUnknown);
}
// Copy the variables_map
friend void store(const parse_command_line& p, variables_map& vm)
{
vm = p.m_vm;
}
// Move the variables_map
friend void store(parse_command_line&& p, variables_map& vm)
{
vm = std::move(p.m_vm);
}
};
// We can define the notify() function as a no-op for our purposes
inline void notify(const variables_map&) {}