Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b1ca8a5
misc: all error msg lowercase
csboo Apr 14, 2025
9f16615
feat(utils): get clean program name
csboo Apr 15, 2025
0805eb4
misc(meson) don't build the static lib, simpler for now
csboo Apr 15, 2025
6513976
BROKEN misc(Arg): better private member names
csboo Apr 15, 2025
d076ad8
BROKEN feat(Arg): getters and setters a A LOT better + more usable
csboo Apr 15, 2025
f688918
BROKEN misc(Arg): better nameing in source file
csboo Apr 15, 2025
ef7c2f8
BROKEN fix(Arg): adjust '<<' overload for new privates
csboo Apr 15, 2025
57eaf45
BROKEN fix(Arg): removed old getter/setter defs
csboo Apr 15, 2025
3403cb5
BROKEN fix(Arg): remove unneeded redeclaration
csboo Apr 15, 2025
84ddf89
BROKEN misc(Arg): use more explicit names again and even more
csboo Apr 15, 2025
a87a18f
BROKEN fix(Arg): reworked the constructor
csboo Apr 15, 2025
4d4b668
BROKEN fix(Arg): clean up includes
csboo Apr 15, 2025
ac7ca5b
BROKEN feat(utils): move src/utils.cpp -> include/utils.hpp
csboo Apr 15, 2025
4f46a22
BROKEN fix: cleaned all includes in all files
csboo Apr 15, 2025
078905f
BROKEN misc(Parser): added a const ref, removed unneeded
csboo Apr 15, 2025
ee66d93
BROKEN fix(Parser): using new arg getters/setters, we gettin' there!
csboo Apr 15, 2025
11d7ca2
fix(Parser): switch to PROGRAM_NAME() util, all fixes done maybe?
csboo Apr 15, 2025
ea9cf88
misc(Arg): rename try_env -> auto_env
csboo Apr 15, 2025
3006b4c
feat(example): WIP added example
csboo Apr 16, 2025
b70152a
refactor(parser): add `this` where possible
jarjk Apr 16, 2025
5425386
fix(parser): know what's static and what's const
jarjk Apr 16, 2025
d6009e9
refactor: dry: `to_upper(string) in utils, other minor
jarjk Apr 16, 2025
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
146 changes: 118 additions & 28 deletions include/Arg.hpp
Original file line number Diff line number Diff line change
@@ -1,55 +1,145 @@
#pragma once

#include "utils.hpp"

#include <cerrno>
#include <ostream>
#include <iostream>
#include <complex>
#include <functional>
#include <optional>
#include <string>

class ClapParser;

class Arg {
public:
explicit Arg(std::string name);
Arg(const std::string& name);

Arg& short_name(const std::string& s);
Arg& long_name(const std::string& l);
Arg& short_name(const std::string& short_name);
Arg& long_name(const std::string& long_name);
Arg& help(const std::string& help);
Arg& required(bool is_required);
Arg& takes_value(bool takes);
Arg& default_value(const std::string& default_val);
Arg& from_env(const char* env_var_name);
Arg& try_env();
void set_try_env_name(const std::string& s);

// Getters
[[nodiscard]] const std::string& short_name() const;
[[nodiscard]] const std::string& long_name() const;
[[nodiscard]] const std::string& help() const;
[[nodiscard]] const std::string& default_value() const;
[[nodiscard]] bool is_required() const;
[[nodiscard]] bool requires_value() const;
Arg& auto_env();

friend std::ostream& operator<<(std::ostream& os, const Arg& arg);

private:
friend class ClapParser;

std::string name_;
std::string short_;
std::string long_;
std::string short_name_;
std::string long_name_;
std::string help_;
bool required_;
bool is_required_;
bool takes_value_;
bool has_env_;
std::string env_name_;
bool try_env_;
std::string try_env_name_;
std::string default_;
bool auto_env_;
// std::string auto_env_name_;
std::string default_value_;
std::optional<std::string> value_;

[[nodiscard]] bool has_default() const;
[[nodiscard]] bool has_env() const;
[[nodiscard]] bool takes_value() const;
[[nodiscard]] const std::string& name() const;
// ----| Getters & Setters |----
// name_
[[nodiscard]] inline const std::string& get__name() const {
return this->name_;
}
inline void set__name(const std::string& name) {
this->name_ = name;
}

// short_
[[nodiscard]] inline const std::string& get__short_name() const {
return this->short_name_;
}
inline void set__short_name(const std::string& short_name) {
this->short_name_ = short_name;
}

// long_
[[nodiscard]] inline const std::string& get__long_name() const {
return this->long_name_;
}
inline void set__long_name(const std::string& long_name) {
this->long_name_ = long_name;
}

// help_
[[nodiscard]] inline const std::string& get__help() const {
return this->help_;
}
inline void set__help(const std::string& help) {
this->help_ = help;
}

// required_
[[nodiscard]] inline bool get__is_required() const {
return this->is_required_;
}
inline void set__is_required(const bool& is_required) {
this->is_required_ = is_required;
}

// takes_value_
[[nodiscard]] inline bool get__takes_value() const {
return this->takes_value_;
}
inline void set__takes_value(const bool& takes_value) {
this->takes_value_ = takes_value;
}

// env_name_
[[nodiscard]] inline const std::string& get__env_name() const {
return this->env_name_;
}
inline void set__env_name(const std::string& env_name) {
this->env_name_ = env_name;
}

// auto_env_
[[nodiscard]] inline bool get__auto_env() const {
return this->auto_env_;
}
inline void set__auto_env(const bool& auto_env) {
this->auto_env_ = auto_env;
}

// auto_env_name_
[[nodiscard]] inline const std::string get__auto_env_name() const {
std::string env_name = PROGRAM_NAME() + '_' + this->get__name();
to_upper(env_name);
return env_name;
}

// default_
[[nodiscard]] inline const std::string& get__default_value() const {
return this->default_value_;
}
inline void set__default_value(const std::string& default_value) {
this->default_value_ = default_value;
}

// value_
[[nodiscard]] inline const std::optional<std::string> get__value() const {
return this->value_;
}
inline void set__value_(const std::string& value) {
this->value_ = value;
}

// ----| Checkers |----
// has_env_
[[nodiscard]] inline bool has_env() const {
return !this->env_name_.empty();
}

// has_default_
[[nodiscard]] inline bool has_default() const {
return !this->default_value_.empty();
}

// has_value_
[[nodiscard]] inline bool has_value() const {
return !this->value_.has_value();
}

};
17 changes: 9 additions & 8 deletions include/Parser.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
#pragma once

#include "Arg.hpp"

#include <iostream>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>

class ClapParser {
public:
void add_arg(const Arg& arg);
void parse(int argc, char* argv[]);
void parse(const int& argc, char* argv[]);
void print_help() const;

template <typename T> std::optional<T> get_one_as(const std::string& name) const;
Expand All @@ -24,16 +25,16 @@ class ClapParser {
std::string program_name_;

// Helper methods
inline bool is_option(const std::string& token) const ;
inline bool is_long_option(const std::string& token) const ;
inline bool is_short_option(const std::string& token) const ;
static inline bool is_option(const std::string& token);
static inline bool is_long_option(const std::string& token);
static inline bool is_short_option(const std::string& token);
const Arg* find_option(const std::string& name) const;
std::vector<Arg> get_positional_args() const;
void apply_defaults();

void parse_options(const std::vector<std::string>& args);
void parse_positional_args(const std::vector<std::string>& args);
void check_required_args();
void check_required_args() const;
void check_env();
void handle_missing_positional(const Arg& arg);

Expand All @@ -45,8 +46,8 @@ class ClapParser {
};

template <typename T> std::optional<T> ClapParser::get_one_as(const std::string& name) const {
auto it = values_.find(name);
if (it == values_.end()) {
auto it = this->values_.find(name);
if (it == this->values_.end()) {
return std::nullopt;
}

Expand Down
24 changes: 18 additions & 6 deletions src/utils.cpp → include/utils.hpp
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
#pragma once

#include <algorithm>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <sstream>

template <typename T, typename E>
T ok_or(std::optional<T> opt, E&& err) {
inline T ok_or(std::optional<T> opt, E&& err) {
if (!opt) std::forward<E>(err)();
return *opt;
}

template <typename T>
T ok_or_throw_str(std::optional<T> opt, const std::string& err) {
inline T ok_or_throw_str(std::optional<T> opt, const std::string& err) {
if (!opt) throw std::runtime_error(err);
return *opt;
}

template <typename T>
T ptr_ok_or_throw_str(T pointer, const std::string& err) {
inline T ptr_ok_or_throw_str(T pointer, const std::string& err) {
if (!pointer) throw std::runtime_error(err);
return pointer;
}

template <typename P, typename E>
E ptr_unwrap_or(P pointer, const E other) {
inline E ptr_unwrap_or(P pointer, const E other) {
if (!pointer) return other;
}

// variadic template function to concatenate any number of arguments
template <typename... Args> std::string concat(Args&&... args) {
template <typename... Args> inline std::string concat(Args&&... args) {
std::ostringstream oss;
(void)std::initializer_list<int>{
(oss << std::forward<Args>(args), 0)...}; // using initializer_list for fold-like behavior
return oss.str();
}

inline void to_upper(std::string &s) {
std::ranges::transform(s, s.begin(), [](const unsigned char& c) { return std::toupper(c); });
}

inline const std::string PROGRAM_NAME() {
const std::string& raw_program_name = program_invocation_name;
return raw_program_name.substr(raw_program_name.rfind('/') + 1);
}
76 changes: 50 additions & 26 deletions meson.build
Original file line number Diff line number Diff line change
@@ -1,41 +1,65 @@
project('claplusplus', 'cpp',
version : '0.0.3',
default_options : [
default_options: [
'cpp_std=c++23',
'warning_level=3',
'werror=true',
# 'werror=true',
'optimization=g',
'debug=true',
'b_ndebug=if-release'
]
)

# Include directory
# Add include directory for headers
include_dir = include_directories('include')

# Library
source_dir = files(
'src/Arg.cpp',
'src/Parser.cpp',
'src/utils.cpp'
)

claplusplus_lib = static_library('claplusplus',
sources: source_dir,
# Build executable directly from all necessary sources
executable('claPlusPlus',
sources: [
'src/example.cpp',
'src/Arg.cpp',
'src/Parser.cpp'
],
include_directories: include_dir,
install: true
install: false
)

# project('claplusplus', 'cpp',
# version : '0.0.3',
# default_options : [
# 'cpp_std=c++23',
# 'warning_level=3',
# 'optimization=g',
# 'debug=true',
# 'b_ndebug=if-release'
# ]
# )

# # Include directory
# include_dir = include_directories('include')

# # Library
# source_dir = files(
# 'src/Arg.cpp',
# 'src/Parser.cpp',
# 'src/utils.cpp'
# )

# claplusplus_lib = static_library('claplusplus',
# sources: source_dir,
# include_directories: include_dir,
# install: true
# )

# Install headers
install_headers(
'include/Arg.hpp',
'include/Parser.hpp',
subdir: 'claplusplus'
)
# # Install headers
# install_headers(
# 'include/Arg.hpp',
# 'include/Parser.hpp',
# subdir: 'claplusplus'
# )

executable('app',
sources : 'src/old_main.cpp',
include_directories: include_dir,
link_with: claplusplus_lib,
install : false
)
# executable('app',
# sources : 'src/old_main.cpp',
# include_directories: include_dir,
# link_with: claplusplus_lib,
# install : false
# )
Loading