Browse Source

* implemented simple parser (to store parameters in simple variables or to invoke a callback)

* implemented member parser (to store parameters in suitable configuration objects)
master
bergmann 5 years ago
parent
commit
98861b74bb
39 changed files with 1596 additions and 313 deletions
  1. +3
    -4
      include/cppargs.h
  2. +9
    -0
      include/cppargs/group.h
  3. +68
    -0
      include/cppargs/group/group.h
  4. +58
    -0
      include/cppargs/group/group.inl
  5. +102
    -0
      include/cppargs/group/member_group.h
  6. +53
    -0
      include/cppargs/group/member_group.inl
  7. +28
    -0
      include/cppargs/group/simple_group.h
  8. +15
    -0
      include/cppargs/group/simple_group.inl
  9. +7
    -0
      include/cppargs/misc.h
  10. +34
    -0
      include/cppargs/misc/context.h
  11. +56
    -0
      include/cppargs/misc/context.inl
  12. +44
    -0
      include/cppargs/misc/misc.h
  13. +17
    -0
      include/cppargs/misc/option_parser.h
  14. +167
    -0
      include/cppargs/misc/option_parser.inl
  15. +0
    -161
      include/cppargs/option.h
  16. +0
    -33
      include/cppargs/option.inl
  17. +6
    -0
      include/cppargs/options.h
  18. +7
    -0
      include/cppargs/options/member.h
  19. +40
    -0
      include/cppargs/options/member/member_option.h
  20. +19
    -0
      include/cppargs/options/member/member_option.inl
  21. +41
    -0
      include/cppargs/options/member/member_predicate_option.h
  22. +21
    -0
      include/cppargs/options/member/member_predicate_option.inl
  23. +52
    -0
      include/cppargs/options/option.h
  24. +12
    -0
      include/cppargs/options/option.inl
  25. +6
    -0
      include/cppargs/options/simple.h
  26. +14
    -0
      include/cppargs/options/simple/simple_option.h
  27. +42
    -0
      include/cppargs/options/simple/simple_predicate_option.h
  28. +21
    -0
      include/cppargs/options/simple/simple_predicate_option.inl
  29. +6
    -93
      include/cppargs/parser.h
  30. +100
    -0
      include/cppargs/parser/member_parser.h
  31. +66
    -0
      include/cppargs/parser/member_parser.inl
  32. +70
    -0
      include/cppargs/parser/parser.h
  33. +95
    -0
      include/cppargs/parser/parser.inl
  34. +78
    -0
      include/cppargs/parser/simple_parser.h
  35. +51
    -0
      include/cppargs/parser/simple_parser.inl
  36. +4
    -0
      src/CMakeLists.txt
  37. +0
    -22
      test/cppargs.cpp
  38. +81
    -0
      test/cppargs/member_parser.cpp
  39. +103
    -0
      test/cppargs/simple_parser.cpp

+ 3
- 4
include/cppargs.h View File

@@ -1,6 +1,5 @@
#pragma once

#include "cppargs/option.h"
#include "cppargs/parser.h"

#include "cppargs/option.inl"
#include <cppargs/misc.h>
#include <cppargs/options.h>
#include <cppargs/parser.h>

+ 9
- 0
include/cppargs/group.h View File

@@ -0,0 +1,9 @@
#pragma once

#include <cppargs/group/group.h>
#include <cppargs/group/simple_group.h>
#include <cppargs/group/member_group.h>

#include <cppargs/group/group.inl>
#include <cppargs/group/simple_group.inl>
#include <cppargs/group/member_group.inl>

+ 68
- 0
include/cppargs/group/group.h View File

@@ -0,0 +1,68 @@
#pragma once

#include <memory>
#include <vector>
#include <cppargs/options/option.h>

namespace cppargs
{

/**
* @brief Struct to collect group meta data.
*/
struct group_meta
{
const std::string name { "" }; //!< Name of the group.
const std::string description { "" }; //!< Description of the group.
};

/**
* @brief Struct to manage options and subgroups.
*/
struct group
{
public:
using option_type = option;
using option_ptr_type = std::unique_ptr<option_type>;
using option_vector_type = std::vector<option_ptr_type>;

using group_type = group;
using group_ptr_type = std::unique_ptr<group_type>;
using group_vector_type = std::vector<group_ptr_type>;

public:
friend struct parser;

const group_meta meta;

protected:
option_vector_type _options;
group_vector_type _groups;

private:
inline group(const group&) = delete;

public:
/**
* @brief Constructor.
*
* @param[in] p_meta Meta data of the group.
* @param[in] p_args Sub groups and options.
*/
template<typename... T_args>
inline group(
const group_meta& p_meta,
T_args&&... p_args);

/**
* @brief Move constructor.
*/
inline group(group&&) = default;

/**
* @brief Destructor.
*/
virtual ~group() = default;
};

}

+ 58
- 0
include/cppargs/group/group.inl View File

@@ -0,0 +1,58 @@
#pragma once

#include <cpputils/mp.h>

#include "group.h"

namespace cppargs
{

template<typename T_arg, typename T_enable = void>
struct group_init;

template<typename T_arg>
struct group_init<
std::unique_ptr<T_arg>,
utl::mp::enable_if<utl::mp::is_base_of<option, T_arg>>>
{
using option_ptr_type = std::unique_ptr<option>;
using option_vector_type = std::vector<option_ptr_type>;
using group_ptr_type = std::unique_ptr<group>;
using group_vector_type = std::vector<group_ptr_type>;

option_vector_type& options;
group_vector_type& groups;

template<typename X>
void operator()(X&& x)
{ options.emplace_back(std::forward<X>(x)); }
};

template<typename T_arg>
struct group_init<
std::unique_ptr<T_arg>,
utl::mp::enable_if<utl::mp::is_base_of<group, T_arg>>>
{
using option_ptr_type = std::unique_ptr<option>;
using option_vector_type = std::vector<option_ptr_type>;
using group_ptr_type = std::unique_ptr<group>;
using group_vector_type = std::vector<group_ptr_type>;

option_vector_type& options;
group_vector_type& groups;

template<typename X>
void operator()(X&& x)
{ groups.emplace_back(std::forward<X>(x)); }
};

template<typename... T_args>
group::group(
const group_meta& p_meta,
T_args&&... p_args)
: meta(p_meta)
{
(void) (int[]) { 0, (group_init<T_args> { _options, _groups } (std::forward<T_args>(p_args)), 0)... };
}

}

+ 102
- 0
include/cppargs/group/member_group.h View File

@@ -0,0 +1,102 @@
#pragma once

#include <cppargs/group/group.h>
#include <cppargs/options/member/member_option.h>

namespace cppargs
{

/**
* @brief Member group that is owned by other groups.
*/
template<typename T_instance>
struct member_owned_group
{
public:
using instance_type = T_instance;
using owner_type = instance_owner<instance_type>;

template<typename, typename, typename>
friend struct member_group;

protected:
owner_type* _owner { nullptr };

public:
/**
* @brief Destructor.
*/
virtual ~member_owned_group() = default;
};

/**
* @brief Member group that is owner of of other groups and options.
*/
template<typename T_instance>
struct member_owner_group
: public instance_owner<T_instance>
{
public:
using instance_type = T_instance;

/**
* @brief Get the instance of current parsed object.
*/
virtual instance_type* instance() const override
{ return nullptr; }

/**
* @brief Destructor.
*/
virtual ~member_owner_group() = default;
};

/**
* @brief Member group that stores subgroups and options.
*/
template<
typename T_owner_instance,
typename T_my_instance,
typename T_resolve_instance_pred>
struct member_group
: public group
, public member_owned_group<T_owner_instance>
, public member_owner_group<T_my_instance>
{
public:
using owner_instance_type = T_owner_instance;
using my_instance_type = T_my_instance;
using resolve_instance_pred_type = T_resolve_instance_pred;
using my_owned_group_type = member_owned_group<owner_instance_type>;
using sub_owned_group_type = member_owned_group<my_instance_type>;
using owner_group_type = member_owner_group<my_instance_type>;
using member_option_type = member_option<my_instance_type>;

private:
resolve_instance_pred_type _predicate;

public:
/**
* @brief Constructor.
*
* @param[in] p_meta Meta data of the group.
* @param[in] p_args Sub groups and options.
*/
template<typename... T_args>
inline member_group(
const group_meta& p_meta,
resolve_instance_pred_type&& p_pred,
T_args&&... p_args);

/**
* @brief Destructor.
*/
virtual ~member_group() = default;

/**
* @brief Get the instance of current parsed object.
*/
virtual my_instance_type* instance() const override;
};

}

+ 53
- 0
include/cppargs/group/member_group.inl View File

@@ -0,0 +1,53 @@
#pragma once

#include <cppargs/group/member_group.h>

namespace cppargs
{

template<
typename T_owner_instance,
typename T_my_instance,
typename T_resolve_instance_pred>
template<
typename... T_args>
member_group<T_owner_instance, T_my_instance, T_resolve_instance_pred>
::member_group(
const group_meta& p_meta,
resolve_instance_pred_type&& p_pred,
T_args&&... p_args)
: group (p_meta, std::forward<T_args>(p_args)...)
, _predicate(p_pred)
{
for (auto& o : _options)
{
auto* x = dynamic_cast<member_option_type*>(o.get());
if (!x)
throw std::runtime_error("option of member group is not a suitable member option");
x->_owner = this;
}

for (auto& g : _groups)
{
auto* x = dynamic_cast<sub_owned_group_type*>(g.get());
if (!x)
throw std::runtime_error("group of member group is not a suitable member group");
x->_owner = this;
}
}

template<
typename T_owner_instance,
typename T_my_instance,
typename T_resolve_instance_pred>
T_my_instance* member_group<T_owner_instance, T_my_instance, T_resolve_instance_pred>
::instance() const
{
T_owner_instance * owner_inst = nullptr;
if (this->_owner)
owner_inst = this->_owner->instance();
auto* ret = _predicate(owner_inst);
return ret;
}

}

+ 28
- 0
include/cppargs/group/simple_group.h View File

@@ -0,0 +1,28 @@
#pragma once

#include <cppargs/group/group.h>

namespace cppargs
{

struct simple_group
: public group
{
private:
template<typename T_arg>
using simple_verify_and_forward = verify_and_forward<simple_group, simple_option, T_arg>;

public:
/**
* @brief Constructor.
*
* @param[in] p_meta Meta data of the group.
* @param[in] p_args Sub groups and options.
*/
template<typename... T_args>
inline simple_group(
const group_meta& p_meta,
T_args&&... p_args);
};

}

+ 15
- 0
include/cppargs/group/simple_group.inl View File

@@ -0,0 +1,15 @@
#pragma once

#include <cppargs/group/simple_group.h>

namespace cppargs
{

template<typename... T_args>
simple_group::simple_group(
const group_meta& p_meta,
T_args&&... p_args)
: group(p_meta, simple_verify_and_forward<T_args> { } (std::forward<T_args>(p_args))...)
{ }

}

+ 7
- 0
include/cppargs/misc.h View File

@@ -0,0 +1,7 @@
#pragma once

#include <cppargs/misc/context.h>
#include <cppargs/misc/option_parser.h>

#include <cppargs/misc/context.inl>
#include <cppargs/misc/option_parser.inl>

+ 34
- 0
include/cppargs/misc/context.h View File

@@ -0,0 +1,34 @@
#pragma once

#include <string>
#include <cstring>

namespace cppargs
{

enum class next_result
{
finished = 0, //!< context does not have any further arguments
option_short = 1, //!< moving to next argument was successfull (argument starts with - or --)
option_long = 2, //!< moving to next argument was successfull (argument starts with - or --)
argument = 3, //!< moving to next argument was successfull (argument does not starts with - or --)
};

struct context
{
int argc;
char const * const * argv;
char const * exe;
char const * arg { nullptr };
char const * value { nullptr };
int index { 0 };

/**
* @brief Move to the next argument
*
* @param[out] key key of the next argument
*/
inline next_result next(std::string* key = nullptr);
};

}

+ 56
- 0
include/cppargs/misc/context.inl View File

@@ -0,0 +1,56 @@
#pragma once

#include "context.h"

namespace cppargs
{

next_result context::next(std::string* key)
{
if (key)
key->clear();

if (index + 1 >= argc)
return next_result::finished;

++index;
arg = argv[index];
value = nullptr;

/* check argument */
int i = 0;
value = nullptr;
while (arg[i] == '-')
++i;

/* parse value */
if (i == 1)
{
if (key)
*key = std::string("-") + arg[1];
if (arg[2] != '\0')
value = &arg[2];
return next_result::option_short;
}
else if (i == 2)
{
auto tmp = strchr(arg, '=');
if (tmp)
{
value = tmp + 1;
if (key)
*key = std::string(arg, static_cast<size_t>(tmp - arg));
}
else if (key)
{
*key = arg;
}
return next_result::option_long;
}
else
{
return next_result::argument;
}
}

}

+ 44
- 0
include/cppargs/misc/misc.h View File

@@ -0,0 +1,44 @@
#pragma once

#include <cpputils/mp.h>

namespace cppargs
{

template<typename T_group, typename T_option, typename T_arg>
struct verify_and_forward
{
template<typename T, typename T_enable = void>
struct check
{
template<typename X>
constexpr X&& operator()(X&& x) const
{ static_assert(sizeof(X) == -1, "invalid group or option type"); }
};

template<typename T>
struct check<
T,
utl::mp::enable_if<utl::mp::is_base_of<T_group, T>>>
{
template<typename X>
constexpr X&& operator()(X&& x) const
{ return std::forward<X>(x); }
};

template<typename T>
struct check<
std::unique_ptr<T>,
utl::mp::enable_if<utl::mp::is_base_of<T_option, T>>>
{
template<typename X>
constexpr X&& operator()(X&& x) const
{ return std::forward<X>(x); }
};

template<typename X>
constexpr decltype(auto) operator()(X&& x) const
{ return check<T_arg> { } (std::forward<X>(x)); }
};

}

+ 17
- 0
include/cppargs/misc/option_parser.h View File

@@ -0,0 +1,17 @@
#pragma once

#include <cppargs/misc/context.h>

namespace cppargs
{

template<typename T_value>
struct option_parser
{
cppargs::context& context;
T_value& value;

inline void operator()() const;
};

}

+ 167
- 0
include/cppargs/misc/option_parser.inl View File

@@ -0,0 +1,167 @@
#pragma once

#include <list>
#include <vector>
#include <cpputils/misc/string.h>
#include <cppargs/misc/option_parser.h>

namespace cppargs
{

template<typename T_derived, typename T_value>
struct option_parser_list_impl;

template<typename T_value>
void option_parser<T_value>::operator()() const
{
auto arg = context.arg;
auto str = context.value;

if (!str)
{
auto ret = context.next();
if (ret != next_result::argument)
throw std::runtime_error(std::string("expected one argument (option=") + arg + ")");
str = context.arg;
}

if (!utl::try_from_string(str, value))
throw std::runtime_error(std::string("unable to parse option argument (option=") + arg + "; value=" + str + ")");
}

template<>
struct option_parser<bool>
{
cppargs::context& context;
bool& value;

inline void operator()() const
{
value = true;
auto arg = context.arg;
auto str = context.value;

if (!str)
{
auto next_context = context;
auto ret = next_context.next();
if (ret == next_result::argument)
{
context = next_context;
str = context.arg;
}
}

if (str && !utl::try_from_string(str, value))
throw std::runtime_error(std::string("unable to parse option argument (option=") + arg + "; value=" + str + ")");
}
};

template<typename T_value>
struct option_parser<std::vector<T_value>>
: public option_parser_list_impl<
option_parser<std::vector<T_value>>, T_value>
{
using value_type = T_value;
using vector_type = std::vector<value_type>;
using this_type = option_parser<vector_type>;
using base_type = option_parser_list_impl<this_type, value_type>;

cppargs::context& context;
vector_type& value;

inline option_parser(
cppargs::context& p_context,
vector_type& p_value)
: base_type (*this)
, context (p_context)
, value (p_value)
{ }

template<typename X>
inline void emplace(X&& x)
{ value.emplace_back(std::forward<X>(x)); }
};

template<typename T_value>
struct option_parser<std::list<T_value>>
: public option_parser_list_impl<
option_parser<std::list<T_value>>, T_value>
{
using value_type = T_value;
using list_type = std::list<value_type>;
using this_type = option_parser<list_type>;
using base_type = option_parser_list_impl<this_type, value_type>;

cppargs::context& context;
list_type& value;

inline option_parser(
cppargs::context& p_context,
list_type& p_value)
: base_type (*this)
, context (p_context)
, value (p_value)
{ }

template<typename X>
inline void emplace(X&& x)
{ value.emplace_back(std::forward<X>(x)); }
};

template<typename T_derived, typename T_value>
struct option_parser_list_impl
{
using derived_type = T_derived;
using value_type = T_value;

derived_type& derived;

inline option_parser_list_impl(
derived_type& p_derived)
: derived(p_derived)
{ }

inline void operator()() const
{
auto arg = derived.context.arg;
auto index = derived.context.index;
if (derived.context.value)
{
const char * s = derived.context.value;
const char * e = s - 1;
do
{
++e;
if ( s != e
&& ( *e == ','
|| *e == '\0'))
{
value_type tmp;
std::string str(s, static_cast<size_t>(e - s));
s = e + 1;
if (!utl::try_from_string(str, tmp))
throw std::runtime_error(std::string("unable to parse option argument (option=") + arg + "; value=" + s + ")");
derived.emplace(std::move(tmp));
}
}
while(*e != '\0');
}
else
{
auto new_context = derived.context;
while (new_context.next() == next_result::argument)
{
value_type tmp;
if (!utl::try_from_string(new_context.arg, tmp))
throw std::runtime_error(std::string("unable to parse option argument (option=") + arg + "; value=" + new_context.arg + ")");
derived.emplace(std::move(tmp));
derived.context = new_context;
}
if (derived.context.index == index)
throw std::runtime_error(std::string("expected at least one argument (option=") + arg + ")");
}
}
};

}

+ 0
- 161
include/cppargs/option.h View File

@@ -1,161 +0,0 @@
#pragma once

#include <string>

namespace cppargs
{

enum class next_result
{
finished = 0, //!< context does not have any further arguments
option_short = 1, //!< moving to next argument was successfull (argument starts with - or --)
option_long = 2, //!< moving to next argument was successfull (argument starts with - or --)
argument = 3, //!< moving to next argument was successfull (argument does not starts with - or --)
};

struct context
{
int const argc;
char const * const * const argv;
char const * const exe;
char const * arg { nullptr };
char const * value { nullptr };
int index { 0 };

inline next_result next(std::string* key = nullptr)
{
if (key)
key->clear();

if (index + 1 >= argc)
return next_result::finished;

++index;
arg = argv[index];
value = nullptr;

/* check argument */
int i = 0;
value = nullptr;
while (arg[i] == '-')
++i;

/* parse value */
if (i == 1)
{
if (key)
*key = std::string("-") + arg[1];
if (arg[2] != '\0')
value = &arg[2];
return next_result::option_short;
}
else if (i == 2)
{
auto tmp = strchr(arg, '=');
if (tmp)
{
value = tmp + 1;
if (key)
*key = std::string(arg, static_cast<size_t>(tmp - arg - 1));
}
else if (key)
{
*key = arg;
}
return next_result::option_long;
}
else
{
return next_result::argument;
}
}
};

struct option
{
public:
const std::string long_name;
const char short_name;

public:
inline option(
const std::string& p_long_name,
const char p_short_name);

virtual ~option() = default;

virtual void parse(context& c) = 0;
};

template<typename T_predicate>
struct predicate_option
: public option
{
public:
using predicate_type = T_predicate;

private:
predicate_type _predicate;

public:
template<typename... T_args>
inline predicate_option(
const std::string& p_long_name,
const char p_short_name,
T_args&&... args);

virtual void parse(context& c) override;
};

template<typename T_value>
struct reference_option
: public option
{
public:
using value_type = T_value;

private:
value_type& _value;

public:
inline reference_option(
const std::string& p_long_name,
const char p_short_name,
value_type& p_value)
: option(p_long_name, p_short_name)
, _value(p_value)
{ }

virtual void parse(context& c) override
{
auto old = c;
auto value = c.value;
if (!value)
{
auto ret = c.next();
if (ret != next_result::argument)
throw std::runtime_error(std::string("option ") + old.arg + " expected one argument");
value = c.arg;
}

std::cout << value << std::endl;
}
};

template<typename T_predicate>
constexpr decltype(auto) make_predicate_option(const std::string& long_name, char short_name, T_predicate&& predicate)
{
using predicate_type = T_predicate;
using predicate_option_type = predicate_option<predicate_type>;
return std::make_unique<predicate_option_type>(long_name, short_name, std::forward<predicate_type>(predicate));
}

template<typename T_value>
constexpr decltype(auto) make_reference_option(const std::string& long_name, char short_name, T_value& value)
{
using value_type = T_value;
using reference_option_type = reference_option<value_type>;
return std::make_unique<reference_option_type>(long_name, short_name, value);
}

}

+ 0
- 33
include/cppargs/option.inl View File

@@ -1,33 +0,0 @@
#pragma once

#include "option.h"

namespace cppargs
{

/* option */

option::option(
const std::string& p_long_name,
const char p_short_name)
: long_name (p_long_name)
, short_name(p_short_name)
{ }

/* predicate_option */

template<typename T_predicate>
template<typename... T_args>
predicate_option<T_predicate>::predicate_option(
const std::string& p_long_name,
const char p_short_name,
T_args&&... args)
: option(p_long_name, p_short_name)
, _predicate(std::forward<T_args>(args)...)
{ }

template<typename T_predicate>
void predicate_option<T_predicate>::parse(context& c)
{ _predicate(c); }

}

+ 6
- 0
include/cppargs/options.h View File

@@ -0,0 +1,6 @@
#pragma once

#include <cppargs/options/option.h>
#include <cppargs/options/simple.h>

#include <cppargs/options/option.inl>

+ 7
- 0
include/cppargs/options/member.h View File

@@ -0,0 +1,7 @@
#pragma once

#include <cppargs/options/member/member_option.h>
#include <cppargs/options/member/member_predicate_option.h>

#include <cppargs/options/member/member_option.inl>
#include <cppargs/options/member/member_predicate_option.inl>

+ 40
- 0
include/cppargs/options/member/member_option.h View File

@@ -0,0 +1,40 @@
#pragma once

#include <cppargs/options/option.h>

namespace cppargs
{
template<typename T_instance>
struct instance_owner
{
virtual T_instance* instance() const = 0;
};

template<typename T_instance>
struct member_option
: public option
{
public:
using base_type = option;
using instance_type = T_instance;
using owner_type = instance_owner<instance_type>;

private:
template<typename X, typename Y, typename Z>
friend struct member_group;

owner_type* _owner;

public:
using option::option;

protected:
/**
* @brief Get the current instance of the object to parse.
*
* @return Current instance of the object to parse.
*/
inline instance_type& instance() const;
};

}

+ 19
- 0
include/cppargs/options/member/member_option.inl View File

@@ -0,0 +1,19 @@
#pragma once

#include <cppargs/options/member/member_option.h>

namespace cppargs
{

template<typename T_instance>
T_instance& member_option<T_instance>::instance() const
{
if (!_owner)
throw std::runtime_error("option has no owner assigned");
auto* ret = _owner->instance();
if (!ret)
throw std::runtime_error("option has no instance assigned");
return *ret;
}

}

+ 41
- 0
include/cppargs/options/member/member_predicate_option.h View File

@@ -0,0 +1,41 @@
#pragma once

#include <cppargs/options/member/member_option.h>

namespace cppargs
{

template<typename T_instance, typename T_value, typename T_predicate>
struct member_predicate_option
: public member_option<T_instance>
{
public:
using instance_type = T_instance;
using value_type = T_value;
using predicate_type = T_predicate;
using base_type = member_option<instance_type>;

private:
predicate_type _predicate;

public:
/**
* @brief Constructor.
*
* @param[in] p_meta Meta data of the option.
* @param[in] args Arguments to pass to the predicate constructor.
*/
template<typename... T_args>
inline member_predicate_option(
const option_meta& p_meta,
T_args&&... args);

/**
* @brief Parse the option using the current context.
*
* @param[in|out] c Context to use for parsing.
*/
virtual void parse(context& c) const override;
};

}

+ 21
- 0
include/cppargs/options/member/member_predicate_option.inl View File

@@ -0,0 +1,21 @@
#pragma once

#include <cppargs/options/member/member_predicate_option.h>

namespace cppargs
{

template<typename T_instance, typename T_value, typename T_predicate>
template<typename... T_args>
member_predicate_option<T_instance, T_value, T_predicate>::member_predicate_option(
const option_meta& p_meta,
T_args&&... args)
: base_type (p_meta)
, _predicate(std::forward<T_args>(args)...)
{ }

template<typename T_instance, typename T_value, typename T_predicate>
void member_predicate_option<T_instance, T_value, T_predicate>::parse(context& c) const
{ _predicate(this->instance(), c); }

}

+ 52
- 0
include/cppargs/options/option.h View File

@@ -0,0 +1,52 @@
#pragma once

#include <string>
#include <cppargs/misc/context.h>

namespace cppargs
{

/**
* @brief Class to store all meta data for a option.
*/
struct option_meta
{
const std::string long_name { "" }; //!< long name of the option (without the prepending --)
const char short_name { '\0' }; //!< short option of the option (without the prependding -)
const std::string description { "" }; //!< description for this option
const std::string default_value { "" }; //!< default value for this option
const bool optional { true }; //!< TRUE if this option is optional, FALSE otherwise
};

/**
* @brief Option to parse from command line arguments
*/
struct option
{
public:
const option_meta meta; //!< meta data of the option

protected:
/**
* @brief Constructor.
*
* @param[in] p_long_name Long name of the option.
* @param[in] p_short_name Short name of the option.
*/
inline option(const option_meta& meta);

public:
/**
* @brief Destructor.
*/
virtual ~option() = default;

/**
* @brief Parse the option using the current context.
*
* @param[in|out] c Context to use for parsing.
*/
virtual void parse(context& c) const = 0;
};

}

+ 12
- 0
include/cppargs/options/option.inl View File

@@ -0,0 +1,12 @@
#pragma once

#include "option.h"

namespace cppargs
{

option::option(const option_meta& p_meta)
: meta(p_meta)
{ }

}

+ 6
- 0
include/cppargs/options/simple.h View File

@@ -0,0 +1,6 @@
#pragma once

#include <cppargs/options/simple/simple_option.h>
#include <cppargs/options/simple/simple_predicate_option.h>

#include <cppargs/options/simple/simple_predicate_option.inl>

+ 14
- 0
include/cppargs/options/simple/simple_option.h View File

@@ -0,0 +1,14 @@
#pragma once

#include <cppargs/options/option.h>

namespace cppargs
{

struct simple_option
: public option
{
using option::option;
};

}

+ 42
- 0
include/cppargs/options/simple/simple_predicate_option.h View File

@@ -0,0 +1,42 @@
#pragma once

#include <string>
#include <cppargs/options/simple/simple_option.h>

namespace cppargs
{

/**
* @brief Specialized option using a predicate to parse the argument.
*/
template<typename T_predicate>
struct simple_predicate_option
: public simple_option
{
public:
using predicate_type = T_predicate;

private:
predicate_type _predicate;

public:
/**
* @brief Constructor.
*
* @param[in] p_meta Meta data of the option.
* @param[in] args Arguments to pass to the predicate constructor.
*/
template<typename... T_args>
inline simple_predicate_option(
const option_meta& p_meta,
T_args&&... args);

/**
* @brief Parse the current argument.
*
* @param[in|out] c Context of the current argument.
*/
virtual void parse(context& c) const override;
};

}

+ 21
- 0
include/cppargs/options/simple/simple_predicate_option.inl View File

@@ -0,0 +1,21 @@
#pragma once

#include "simple_predicate_option.h"

namespace cppargs
{

template<typename T_predicate>
template<typename... T_args>
simple_predicate_option<T_predicate>::simple_predicate_option(
const option_meta& p_meta,
T_args&&... args)
: simple_option(p_meta)
, _predicate(std::forward<T_args>(args)...)
{ }

template<typename T_predicate>
void simple_predicate_option<T_predicate>::parse(context& c) const
{ _predicate(c); }

}

+ 6
- 93
include/cppargs/parser.h View File

@@ -1,96 +1,9 @@
#pragma once

#include <map>
#include <vector>
#include <memory>
#include <cstring>
#include <cppargs/parser/parser.h>
#include <cppargs/parser/simple_parser.h>
#include <cppargs/parser/member_parser.h>

#include "option.h"

namespace cppargs
{

struct parser
{
private:
using option_ptr_type = std::unique_ptr<option>;
using option_vector_type = std::vector<option_ptr_type>;
using option_map_type = std::map<std::string, option*>;

private:
option_ptr_type _default;
option_vector_type _options;
option_map_type _map;

inline void update_map()
{
_map.clear();
for (auto& o : _options)
{
if (!o)
throw std::runtime_error("expectd option to not be null!");
if (o->short_name)
_map.emplace(std::string("-") + o->short_name, o.get());
if (!o->long_name.empty())
_map.emplace(std::string("--") + o->long_name, o.get());
}
}

inline void handle_invalid_arg(context& c) const
{
if (_default)
{
_default->parse(c);
}
else
{
throw std::runtime_error(std::string("invalid or unknown argument: ") + c.arg);
}
}

public:
template<typename... T_option>
inline parser(option_ptr_type&& default_option, T_option&&... option)
: _default(std::move(default_option))
{
(void) (int[]) { 0, (_options.emplace_back(std::forward<T_option>(option)), 0)... };
update_map();
}

void parse(int argc, char const * argv[]) const
{
if (argc <= 0) {
return;
}

context c
{
argc,
argv,
argv[0],
};

next_result ret;
std::string key;
while ((ret = c.next(&key)) != next_result::finished)
{
/* move to next option */
if ( ret != next_result::option_short
&& ret != next_result::option_long)
handle_invalid_arg(c);

/* find option */
auto it = _map.find(key);
if (it == _map.end())
{
handle_invalid_arg(c);
}
else
{
it->second->parse(c);
}
}
}
};

}
#include <cppargs/parser/parser.inl>
#include <cppargs/parser/simple_parser.inl>
#include <cppargs/parser/member_parser.inl>

+ 100
- 0
include/cppargs/parser/member_parser.h View File

@@ -0,0 +1,100 @@
#pragma once

#include <cppargs/parser/parser.h>
#include <cppargs/group/member_group.h>

namespace cppargs
{

struct member_parser_base
: public parser
{
public:
using parser::parser;

public:
/**
* @brief Create new member option.
*
* @param[in] meta Meta data of the new option.
* @param[in] member Member to write parsed argument to.
*
* @return Created option.
*/
template<typename T_instance, typename T_object, typename T_value>
static constexpr decltype(auto) make_member_option(const option_meta& meta, T_value T_object::*member);

/**
* @brief Create new group.
*
* @param[in] meta Meta data of the new option.
* @param[in] member Member of current instance to use for this group.
* @param[in] args Subgroups and options.
*
* @return Created group.
*/
template<typename T_instance, typename T_object, typename T_value, typename... T_args>
static constexpr decltype(auto) make_member_group(const group_meta& meta, T_value T_object::*member, T_args&&... args);
};

/**
* @brief Class to parse options using a object to store values in.
*/
template<typename T_instance>
struct member_parser
: public member_parser_base
{
public:
using base_type = member_parser_base;
using instance_type = T_instance;
using this_type = member_parser<instance_type>;
using option_ptr_type = typename base_type::option_ptr_type;

private:
struct base_group_pred
{
this_type& owner;

instance_type* operator()(void*) const
{ return owner._instance; }
};

using base_group_type = member_group<void, instance_type, base_group_pred>;

template<typename... T_args>
constexpr decltype(auto) make_base_group(T_args&&... args)
{
using owner_instance_type = void;
using group_instance_type = T_instance;
using predicate_type = base_group_pred;
using group_type = member_group<owner_instance_type, group_instance_type, predicate_type>;
return group_type({ }, predicate_type { *this }, std::forward<T_args>(args)...);
}

private:
base_group_type _group;
mutable instance_type* _instance { nullptr };

public:
/**
* @brief Constructor.
*
* @param[in] p_default Default option to use for unknown arguents (optional / can be null).
* @param[in] p_args Options and subgroups.
*/
template<typename... T_args>
member_parser(
option_ptr_type&& p_default,
T_args&&... args);

/**
* @brief Parse the command line arguments using the passed options.
*
* @param[in] instance Instance to store parsed values in.
* @param[in] argc Number of arguments in argv.
* @param[in] argv Array of all arguments.
*/
inline void parse(instance_type& instance, int argc, char const * argv[]) const;
};

}

+ 66
- 0
include/cppargs/parser/member_parser.inl View File

@@ -0,0 +1,66 @@
#pragma once

#include <cppargs/parser/parser.h>
#include <cppargs/options/member.h>
#include <cppargs/misc/option_parser.h>
#include <cppargs/group/member_group.inl>

namespace cppargs
{

template<typename T_instance, typename T_object, typename T_value>
constexpr decltype(auto) member_parser_base::make_member_option(const option_meta& meta, T_value T_object::*member)
{
static_assert(std::is_base_of<T_object, T_instance>::value, "Member pointer must be in the instance type.");

using instance_type = T_instance;
using value_type = T_value;

auto predicate = [member](instance_type& instance, context& c)
{
using executor_type = option_parser<value_type>;
executor_type { c, instance.*member } ( );
};

using predicate_type = decltype(predicate);
using member_predicate_option_type = member_predicate_option<instance_type, value_type, predicate_type>;
return std::make_unique<member_predicate_option_type>(meta, std::move(predicate));
}

template<typename T_instance, typename T_object, typename T_value, typename... T_args>
constexpr decltype(auto) member_parser_base::make_member_group(const group_meta& meta, T_value T_object::*member, T_args&&... args)
{
using owner_instance_type = T_instance;
using my_instance_type = T_value;

auto predicate = [member](owner_instance_type* instance)
{
if (!instance)
throw std::runtime_error("unable to resolve instance for sub group: no instance assigned");
return &(instance->*member);
};

using predicate_type = decltype(predicate);
using group_type = member_group<owner_instance_type, my_instance_type, predicate_type>;
return std::make_unique<group_type>(meta, std::move(predicate), std::forward<T_args>(args)...);
}

template<typename T_instance>
template<typename... T_args>
member_parser<T_instance>::member_parser(
option_ptr_type&& p_default,
T_args&&... args)
: base_type (std::move(p_default), _group)
, _group (make_base_group(std::forward<T_args>(args)...))
{ update_map(); }

template<typename T_instance>
void member_parser<T_instance>::parse(instance_type& instance, int argc, char const * argv[]) const
{
auto lambda = [this]() { _instance = nullptr; };
auto cleanup = std::make_unique<decltype(lambda)>(std::move(lambda));
_instance = &instance;
base_type::parse(argc, argv);
}

}

+ 70
- 0
include/cppargs/parser/parser.h View File

@@ -0,0 +1,70 @@
#pragma once

#include <map>
#include <vector>
#include <memory>
#include <cstring>
#include <cppargs/group/group.h>

namespace cppargs
{

/**
* @brief Class to parse command line arguments.
*/
struct parser
{
public:
using option_type = option;
using option_ptr_type = std::unique_ptr<option_type>;
using option_map_type = std::map<std::string, option_type*>;

private:
group& _group;
option_ptr_type _default; //!< Default option to parse unknown arguments.
option_map_type _map; //!< Map of option name to option.

protected:
/**
* @brief Add the passed group to the options map
*/
inline void add_to_map(const group& g);

/**
* @brief Update the option map.
*/
inline void update_map();

/**
* @brief Handle invalid argument (use default option or throw exception).
*/
inline void handle_invalid_arg(context& c) const;

/**
* @brief Parse the command line arguments using the passed options.
*
* @param[in] argc Number of arguments in argv.
* @param[in] argv Array of all arguments.
*/
inline void parse(int argc, char const * argv[]) const;

public:
/**
* @brief Constructor.
*
* @param[in] p_default Default option to use for unknown arguents (optional / can be null).
* @param[in] p_group Group with options and subgroups.
*/
inline parser(
option_ptr_type&& p_default,
group& p_group);

/**
* @brief Print the help of all options
*
* @param[in] os Stream to write help to.
*/
void print_help(std::ostream& os) const;
};

}

+ 95
- 0
include/cppargs/parser/parser.inl View File

@@ -0,0 +1,95 @@
#pragma once

#include <cppargs/parser/parser.h>
#include <cppargs/group/group.inl>

namespace cppargs
{

parser::parser(
option_ptr_type&& p_default,
group& p_group)
: _default (std::move(p_default))
, _group (p_group)
{ }

void parser::add_to_map(const group& g)
{
for (auto& o : g._options)
{
if (!o)
throw std::runtime_error("expectd option to not be null!");
if (o->meta.short_name)
{
if (!_map.emplace(std::string("-") + o->meta.short_name, o.get()).second)
throw std::runtime_error(std::string("duplicate option key: ") + o->meta.short_name);
}
if (!o->meta.long_name.empty())
{
if (!_map.emplace(std::string("--") + o->meta.long_name, o.get()).second)
throw std::runtime_error(std::string("duplicate option key: ") + o->meta.long_name);
}
}

for (auto& i : g._groups)
{
if (!i)
throw std::runtime_error("expectd group to not be null!");
add_to_map(*i);
}
}

void parser::update_map()
{
_map.clear();
add_to_map(_group);
}

void parser::handle_invalid_arg(context& c) const
{
if (_default)
{
_default->parse(c);
}
else
{
throw std::runtime_error(std::string("invalid or unknown argument: ") + c.arg);
}
}

void parser::parse(int argc, char const * argv[]) const
{
if (argc <= 0) {
return;
}

context c
{
argc,
argv,
argv[0],
};

next_result ret;
std::string key;
while ((ret = c.next(&key)) != next_result::finished)
{
/* move to next option */
if ( ret != next_result::option_short
&& ret != next_result::option_long)
handle_invalid_arg(c);

/* find option */
auto it = _map.find(key);
if (it == _map.end())
{
handle_invalid_arg(c);
}
else
{
it->second->parse(c);
}
}
}

}

+ 78
- 0
include/cppargs/parser/simple_parser.h View File

@@ -0,0 +1,78 @@
#pragma once

#include <cppargs/misc/misc.h>
#include <cppargs/parser/parser.h>
#include <cppargs/group/simple_group.h>

namespace cppargs
{

/**
* @brief Class to parse options using simple types or callbacks.
*/
struct simple_parser
: public parser
{
public:
using base_type = parser;
using option_ptr_type = typename base_type::option_ptr_type;

private:
simple_group _group;

public:
/**
* @brief Constructor.
*
* @param[in] p_default Default option to use for unknown arguents (optional / can be null).
* @param[in] p_args Options and subgroups.
*/
template<typename... T_args>
simple_parser(
option_ptr_type&& p_default,
T_args&&... args);

/**
* @brief Parse the command line arguments using the passed options.
*
* @param[in] argc Number of arguments in argv.
* @param[in] argv Array of all arguments.
*/
inline void parse(int argc, char const * argv[]) const;

public:
/**
* @brief Create new option group.
*
* @param[in] meta Meta data of the new group.
* @param[in] args Subgroups and options.
*
* @return Created group.
*/
template<typename... T_args>
static constexpr decltype(auto) make_group(const group_meta& meta, T_args&&... args);

/**
* @brief Create new predicate option.
*
* @param[in] meta Meta data of the new option.
* @param[in] predicate Predicate to execute for this option.
*
* @return Created option.
*/
template<typename T_predicate>
static constexpr decltype(auto) make_predicate_option(const option_meta& meta, T_predicate&& predicate);

/**
* @brief Create new reference option.
*
* @param[in] meta Meta data of the new option.
* @param[in] value Value to set for this option.
*
* @return Created option.
*/
template<typename T_value>
static constexpr decltype(auto) make_reference_option(const option_meta& meta, T_value& value);
};

}

+ 51
- 0
include/cppargs/parser/simple_parser.inl View File

@@ -0,0 +1,51 @@
#pragma once

#include <cpputils/mp.h>
#include <cppargs/parser/parser.h>
#include <cppargs/group/simple_group.inl>

namespace cppargs
{

template<typename... T_args>
simple_parser::simple_parser(
option_ptr_type&& p_default,
T_args&&... args)
: base_type (std::move(p_default), _group)
, _group ({ }, std::forward<T_args>(args)...)
{ update_map(); }

void simple_parser::parse(int argc, char const * argv[]) const
{ parser::parse(argc, argv); }

template<typename... T_args>
constexpr decltype(auto) simple_parser::make_group(const group_meta& meta, T_args&&... args)
{
return std::make_unique<simple_group>(meta, std::forward<T_args>(args)...);
}

template<typename T_predicate>
constexpr decltype(auto) simple_parser::make_predicate_option(const option_meta& meta, T_predicate&& predicate)
{
using predicate_type = T_predicate;
using simple_predicate_option_type = simple_predicate_option<predicate_type>;
return std::make_unique<simple_predicate_option_type>(meta, std::forward<predicate_type>(predicate));
}

template<typename T_value>
constexpr decltype(auto) simple_parser::make_reference_option(const option_meta& meta, T_value& value)
{
using value_type = T_value;

auto predicate = [&value](context& c)
{
using executor_type = option_parser<value_type>;
executor_type { c, value } ( );
};

using predicate_type = decltype(predicate);
using simple_predicate_option_type = simple_predicate_option<predicate_type>;
return std::make_unique<simple_predicate_option_type>(meta, std::move(predicate));
}

}

+ 4
- 0
src/CMakeLists.txt View File

@@ -8,11 +8,15 @@ Set ( CMAKE_CXX_STANDARD 17 )

# Project: cppargs ################################################################################

Find_Package ( cpputils REQUIRED )

Project ( cppargs VERSION 1.0.0.0 LANGUAGES CXX )
Set ( CPPARGS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include )
Add_Library ( cppargs INTERFACE )
Target_Include_Directories ( cppargs
INTERFACE ${CPPARGS_INCLUDE_DIR} )
Target_Link_Libraries ( cppargs
INTERFACE cpputils )

If ( HAS_PEDANTIC )
Pedantic_Apply_Flags ( ALL )


+ 0
- 22
test/cppargs.cpp View File

@@ -1,22 +0,0 @@
#include <gtest/gtest.h>

#include <cppargs.h>

TEST(cppargs, simple)
{
int x;
cppargs::parser p(
nullptr,
cppargs::make_reference_option("index", 'i', x),
cppargs::make_predicate_option("test", 't', [](auto& c){
std::cout << c.arg << std::endl;
})
);

std::vector<const char*> args({
"/fuu/bar/exe",
"-i", "6",
"--test"
});
p.parse(static_cast<int>(args.size()), args.data());
}

+ 81
- 0
test/cppargs/member_parser.cpp View File

@@ -0,0 +1,81 @@
#include <list>
#include <vector>
#include <gtest/gtest.h>

#include <cppargs.h>

struct simple
{
int i;
std::string s;
double d;
};

TEST(member_parser, simple_object)
{
using parser = ::cppargs::member_parser<simple>;

simple s;
parser p(
nullptr,
parser::make_member_option<simple>({ "int", 'i' }, &simple::i),
parser::make_member_option<simple>({ "string", 's' }, &simple::s),
parser::make_member_option<simple>({ "double", 'd' }, &simple::d)
);

std::vector<const char*> args({
"/fuu/bar/exe",
"-i", "123",
"--string", "fuubar",
"-d123.123"
});
p.parse(s, static_cast<int>(args.size()), args.data());

EXPECT_EQ(s.i, 123);
EXPECT_EQ(s.s, "fuubar");
EXPECT_EQ(s.d, 123.123);
}

struct grouped
: public simple
{
struct group1
{
std::list<std::string> list;
std::vector<int> vec;
};

group1 g1;
};

TEST(member_parser, grouped_object)
{
using parser = ::cppargs::member_parser<grouped>;

grouped g;
parser p(
nullptr,
parser::make_member_option<grouped>({ "int", 'i' }, &grouped::i),
parser::make_member_option<grouped>({ "string", 's' }, &grouped::s),
parser::make_member_option<grouped>({ "double", 'd' }, &grouped::d),
parser::make_member_group<grouped> ({ }, &grouped::g1,
parser::make_member_option<grouped::group1>({ "list", 'l' }, &grouped::group1::list),
parser::make_member_option<grouped::group1>({ "vector", 'v' }, &grouped::group1::vec))
);

std::vector<const char*> args({
"/fuu/bar/exe",
"-i", "123",
"--string", "fuubar",
"-d123.123",
"--list=fuu,bar,baz",
"--vector", "123", "456", "789"
});
p.parse(g, static_cast<int>(args.size()), args.data());

EXPECT_EQ(g.i, 123);
EXPECT_EQ(g.s, "fuubar");
EXPECT_EQ(g.d, 123.123);
EXPECT_EQ(g.g1.list, std::list<std::string>({ "fuu", "bar", "baz" }));
EXPECT_EQ(g.g1.vec, std::vector<int>({ 123, 456, 789 }));
}

+ 103
- 0
test/cppargs/simple_parser.cpp View File

@@ -0,0 +1,103 @@
#include <vector>
#include <gtest/gtest.h>

#include <cppargs.h>

using parser = ::cppargs::simple_parser;

TEST(simple_parser, short_name_seperate_arg)
{

std::string s;
parser p(
nullptr,
parser::make_reference_option({ "string", 's' }, s)
);

std::vector<const char*> args({
"/fuu/bar/exe", "-s", "fuu"
});
p.parse(static_cast<int>(args.size()), args.data());

EXPECT_EQ(s, "fuu");
}

TEST(simple_parser, long_name_seperate_arg)
{
int i;
parser p(
nullptr,
parser::make_reference_option({ "int", 'i' }, i)
);

std::vector<const char*> args({
"/fuu/bar/exe", "--int", "123"
});
p.parse(static_cast<int>(args.size()), args.data());

EXPECT_EQ(i, 123);
}

TEST(simple_parser, short_name_same_arg)
{
double d;
parser p(
nullptr,
parser::make_reference_option({ "double", 'd' }, d)
);

std::vector<const char*> args({
"/fuu/bar/exe", "-d0.2"
});
p.parse(static_cast<int>(args.size()), args.data());

EXPECT_EQ(d, 0.2);
}

TEST(simple_parser, long_name_same_arg)
{
std::string s;
parser p(
nullptr,
parser::make_reference_option({ "string", 's' }, s)
);

std::vector<const char*> args({
"/fuu/bar/exe", "--string=fuubar"
});
p.parse(static_cast<int>(args.size()), args.data());

EXPECT_EQ(s, "fuubar");
}

TEST(simple_parser, vector_extra_arg)
{
std::vector<int> vec;
parser p(
parser::make_predicate_option({ }, [](auto){ }),
parser::make_reference_option({ "vec", 'v' }, vec)
);

std::vector<const char*> args({
"/fuu/bar/exe", "--vec", "1", "2", "3", "4", "5", "-i"
});
p.parse(static_cast<int>(args.size()), args.data());

EXPECT_EQ(vec, std::vector<int>({ 1, 2, 3, 4, 5 }));
}

TEST(simple_parser, vector_same_arg)
{
std::vector<int> vec;
parser p(
parser::make_predicate_option({ }, [](auto){ }),
parser::make_reference_option({ "vec", 'v' }, vec)
);

std::vector<const char*> args({
"/fuu/bar/exe", "--vec=1,2,3,4,5", "-i"
});
p.parse(static_cast<int>(args.size()), args.data());

EXPECT_EQ(vec, std::vector<int>({ 1, 2, 3, 4, 5 }));
}

Loading…
Cancel
Save