Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F82305124
parser.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Sep 10, 16:24
Size
28 KB
Mime Type
text/x-c
Expires
Thu, Sep 12, 16:24 (2 d)
Engine
blob
Format
Raw Data
Handle
20680363
Attached To
rSPECMICP SpecMiCP / ReactMiCP
parser.cpp
View Options
/* =============================================================================
Copyright (c) 2014 - 2016
F. Georget <fabieng@princeton.edu> Princeton University
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
============================================================================= */
#include "parser.hpp"
#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
#include "../compat.hpp"
namespace
specmicp
{
namespace
cli
{
//! \internal
//! \brief The different types that a value can solve
//!
//! To allow a straigthforward use of the union, only a pointer
//! to the string is stored, the actual string must be stored elsewhere
union
SPECMICP_DLL_LOCAL
OptionValue
{
bool
boolean
;
int
integer
;
double
floating_number
;
std
::
string
*
str
;
OptionValue
()
:
str
(
nullptr
)
{}
};
//! \internal
//! \brief Base class for an option
struct
SPECMICP_DLL_LOCAL
BaseOption
{
BaseOption
(
ValueType
type_value
,
const
std
::
string
&
help
)
:
is_required
(
true
),
value_type
(
type_value
),
help_message
(
help
)
{}
BaseOption
(
ValueType
type_value
,
const
std
::
string
&
help
,
OptionValue
default_value
)
:
is_required
(
false
),
value_type
(
type_value
),
help_message
(
help
),
value
(
default_value
)
{}
bool
is_required
;
bool
is_set
{
false
};
ValueType
value_type
;
std
::
string
help_message
;
OptionValue
value
;
};
//! \internal
//! \brief A positional argument
struct
SPECMICP_DLL_LOCAL
PositionalArgument:
public
BaseOption
{
PositionalArgument
(
const
std
::
string
&
arg_name
,
ValueType
type_value
,
const
std
::
string
&
help
)
:
BaseOption
(
type_value
,
help
),
name
(
arg_name
)
{}
std
::
string
name
;
};
//! \internal
//! \brief An option
//!
//! An option value is precedented by a flag (starting with '--' pr '-')
//! In case of a boolean, only the flag is necessary, the value is implicetely
//! true
//!
struct
SPECMICP_DLL_LOCAL
SwitchArgument:
public
BaseOption
{
SwitchArgument
(
char
the_short_flag
,
const
std
::
string
&
the_long_flag
,
ValueType
value_type
,
OptionValue
default_value
,
const
std
::
string
&
help_message
)
:
BaseOption
(
value_type
,
help_message
,
default_value
),
short_flag
(
the_short_flag
),
long_flag
(
the_long_flag
)
{}
SwitchArgument
(
char
the_short_flag
,
const
std
::
string
&
the_long_flag
,
ValueType
value_type
,
const
std
::
string
&
help_message
)
:
BaseOption
(
value_type
,
help_message
),
short_flag
(
the_short_flag
),
long_flag
(
the_long_flag
)
{}
char
short_flag
;
std
::
string
long_flag
;
};
//! \internal
//! \brief Implementation details for the Command line parser
struct
SPECMICP_DLL_LOCAL
CommandLineParser
::
CommandLineParserImpl
{
//! \brief Option : <flag,value>
using
str_option
=
std
::
pair
<
std
::
string
,
std
::
string
>
;
//! \brief Type of the list of options
using
list_switch_option
=
std
::
vector
<
SwitchArgument
>
;
//! \brief Type of the list of positional argument
using
list_pos_argument
=
std
::
vector
<
PositionalArgument
>
;
//! \brief Type of the index in a list of argument
using
size_type
=
list_pos_argument
::
size_type
;
//! \brief Type of a list of unparsed options
using
raw_list_option
=
std
::
vector
<
std
::
string
>
;
CommandLineParserImpl
()
{}
//! \brief Set the values of the options
void
set_list_str_option
(
const
raw_list_option
&
options
);
//! \brief Check the values of the options/arguments
bool
check_values
();
//! \brief Check the values of the positional arguments
bool
check_pos_arguments
();
//! \brief Check the values of the options
bool
check_options
();
//! \brief Return the index of a short option
//!
//! Return -1 if the option does not exist
int
get_short_option_index
(
char
flag
)
noexcept
;
//! \brief Return the index of a long option
//!
//! Return -1 if the option does not exist
int
get_long_option_index
(
const
std
::
string
&
flag
)
noexcept
;
//! \brief Return the index of a long option
//!
//! \throw runtime_error if the option does not exist
size_t
get_safe_long_option_index
(
const
std
::
string
&
flag
);
//! \brief Return the index of a positional argument
//!
//! \throw runtime error if the option does not exist
size_t
get_safe_pos_argument_index
(
const
std
::
string
&
name
);
//! \brief Parse a boolean option activated by its short flag
size_t
analyse_boolean_short_option
(
size_t
ind
,
char
opts
);
//! \brief Parse an option activated by its short flag
size_t
analyse_short_option
(
size_t
ind
,
const
raw_list_option
&
options
,
char
opts
);
//! \brief Parse an option activated by its long flag
size_t
analyse_long_option
(
size_t
ind
,
const
raw_list_option
&
options
,
const
std
::
string
&
opts
);
//! \brief Parse a positional argument
size_t
analyse_pos_argument
(
size_t
ind
,
const
std
::
string
&
arg_value
);
//! \brief Return the index of the next free positional argument
//!
//! Return -1 if no such argument exists
int
next_free_pos_arg
()
noexcept
;
//! \brief Return a value that can be stored inside an option
//!
//! \tparam T true C++ type of the type
//! \param value the value to set
//! \param value_type the type of the option
template
<
typename
T
>
OptionValue
set_value
(
const
T
&
value
,
ValueType
value_type
);
//! \brief Set the value of the option
template
<
typename
T
>
void
set_option
(
BaseOption
&
arg
,
const
T
&
value
);
//! \print the help message
void
print_help_message
();
std
::
string
m_name
{
"<program name>"
};
//!< Name of the program
std
::
string
m_help_msg
{
"<no help message defined>"
};
//! Global help message
list_switch_option
m_list_opt
;
//!< List of options
list_pos_argument
m_list_args
;
//!< List of positional argument
//! \brief Store the values of the string options
//!
//! This list will store the values of the options
std
::
vector
<
std
::
string
>
m_option_string_values
;
};
//! \name simple_predicator
//! \brief simple functions to chech the type of a string in argv
//!
//! \internal
//! @{
inline
bool
is_non_null
(
const
std
::
string
&
option
)
{
return
(
option
.
length
()
>
0
);
}
inline
bool
is_flag
(
const
std
::
string
&
option
)
{
return
(
is_non_null
(
option
)
&&
option
[
0
]
==
'-'
);
}
inline
bool
is_value
(
const
std
::
string
&
option
)
{
return
(
is_non_null
(
option
)
&&
option
[
0
]
!=
'-'
);
}
inline
bool
is_short_flag
(
const
std
::
string
&
option
)
{
return
(
is_flag
(
option
)
&&
option
.
length
()
>=
2
and
option
[
1
]
!=
'-'
);
}
inline
bool
is_unique_short_flag
(
const
std
::
string
&
option
)
{
return
(
is_flag
(
option
)
&&
option
.
length
()
==
2
and
option
[
1
]
!=
'-'
);
}
inline
bool
is_same_short_flag
(
const
std
::
string
&
option
,
char
flag
)
{
return
(
is_unique_short_flag
(
option
)
and
option
[
'1'
]
==
flag
);
}
inline
bool
is_multiple_short_flags
(
const
std
::
string
&
option
)
{
return
(
is_short_flag
(
option
)
and
option
.
length
()
>
2
);
}
inline
bool
is_long_flag
(
const
std
::
string
&
option
)
{
return
(
is_flag
(
option
)
and
option
.
length
()
>
2
and
option
[
1
]
==
'-'
);
}
inline
bool
is_same_long_flag
(
const
std
::
string
&
option
,
const
std
::
string
&
flag
)
{
return
(
is_long_flag
(
option
)
and
(
option
.
length
()
-
2
)
==
flag
.
length
()
and
option
.
substr
(
2
,
option
.
length
()
-
2
)
==
flag
);
}
//! @}
//! \brief A pair of <key,value> for an option
using
option_value_t
=
std
::
pair
<
std
::
string
,
std
::
string
>
;
//! \brief Split a long option in a pair<option,value> (if = sign in it)
option_value_t
SPECMICP_DLL_LOCAL
split_long_option
(
const
std
::
string
&
opts
);
//! \brief Split a short option
std
::
vector
<
char
>
SPECMICP_DLL_LOCAL
split_short_options
(
const
std
::
string
&
opts
);
// Definition of main functions
CommandLineParser
::
CommandLineParser
()
:
m_impl
(
make_unique
<
CommandLineParserImpl
>
())
{
}
CommandLineParser
::~
CommandLineParser
()
{}
// Getter / Setter
// ==============
// /!\ templated code : needs to be in that order to
// avoid instanciation of a specialization before we define it
// CommandLineParser::get_option
// -----------------------------
template
<>
auto
CommandLineParser
::
get_option
<
ValueType
::
boolean
>
(
const
std
::
string
&
long_flag
)
->
typename
TrueValueType
<
ValueType
::
boolean
>::
type
{
size_t
ind
=
m_impl
->
get_safe_long_option_index
(
long_flag
);
auto
&
opt
=
m_impl
->
m_list_opt
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
boolean
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected boolean."
);
}
return
(
opt
.
value
.
boolean
);
}
template
<>
auto
CommandLineParser
::
get_option
<
ValueType
::
integer
>
(
const
std
::
string
&
long_flag
)
->
typename
TrueValueType
<
ValueType
::
integer
>::
type
{
size_t
ind
=
m_impl
->
get_safe_long_option_index
(
long_flag
);
auto
&
opt
=
m_impl
->
m_list_opt
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
integer
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected integer."
);
}
return
(
opt
.
value
.
integer
);
}
template
<>
auto
CommandLineParser
::
get_option
<
ValueType
::
floating
>
(
const
std
::
string
&
long_flag
)
->
typename
TrueValueType
<
ValueType
::
floating
>::
type
{
size_t
ind
=
m_impl
->
get_safe_long_option_index
(
long_flag
);
auto
&
opt
=
m_impl
->
m_list_opt
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
floating
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected floating number."
);
}
return
(
opt
.
value
.
floating_number
);
}
template
<>
auto
CommandLineParser
::
get_option
<
ValueType
::
string
>
(
const
std
::
string
&
long_flag
)
->
typename
TrueValueType
<
ValueType
::
string
>::
type
{
size_t
ind
=
m_impl
->
get_safe_long_option_index
(
long_flag
);
auto
&
opt
=
m_impl
->
m_list_opt
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
string
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected string."
);
}
std
::
string
*
str_ptr
=
opt
.
value
.
str
;
if
(
str_ptr
==
nullptr
)
{
throw
std
::
runtime_error
(
"Value not set !"
);
}
return
*
(
opt
.
value
.
str
);
}
// CommandLineParser.get_pos_argument
// -----------------------------------
template
<>
auto
CommandLineParser
::
get_pos_argument
<
ValueType
::
boolean
>
(
const
std
::
string
&
name
)
->
typename
TrueValueType
<
ValueType
::
boolean
>::
type
{
size_t
ind
=
m_impl
->
get_safe_pos_argument_index
(
name
);
auto
&
opt
=
m_impl
->
m_list_args
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
boolean
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected boolean."
);
}
return
(
opt
.
value
.
boolean
);
}
template
<>
auto
CommandLineParser
::
get_pos_argument
<
ValueType
::
integer
>
(
const
std
::
string
&
name
)
->
typename
TrueValueType
<
ValueType
::
integer
>::
type
{
size_t
ind
=
m_impl
->
get_safe_pos_argument_index
(
name
);
auto
&
opt
=
m_impl
->
m_list_args
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
integer
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected integer."
);
}
return
(
opt
.
value
.
integer
);
}
template
<>
auto
CommandLineParser
::
get_pos_argument
<
ValueType
::
floating
>
(
const
std
::
string
&
name
)
->
typename
TrueValueType
<
ValueType
::
floating
>::
type
{
size_t
ind
=
m_impl
->
get_safe_pos_argument_index
(
name
);
auto
&
opt
=
m_impl
->
m_list_args
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
floating
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected floating number."
);
}
return
(
opt
.
value
.
floating_number
);
}
template
<>
auto
CommandLineParser
::
get_pos_argument
<
ValueType
::
string
>
(
const
std
::
string
&
name
)
->
typename
TrueValueType
<
ValueType
::
string
>::
type
{
size_t
ind
=
m_impl
->
get_safe_pos_argument_index
(
name
);
auto
&
opt
=
m_impl
->
m_list_args
[
ind
];
if
(
opt
.
value_type
!=
ValueType
::
string
)
{
throw
std
::
runtime_error
(
"Not the right type ! Expected string."
);
}
std
::
string
*
str_ptr
=
opt
.
value
.
str
;
if
(
str_ptr
==
nullptr
)
{
throw
std
::
runtime_error
(
"Value not set !"
);
}
return
*
(
opt
.
value
.
str
);
}
template
<
typename
T
>
T
get_value
(
const
OptionValue
&
u_value
,
ValueType
value_type
)
{
T
value
;
switch
(
value_type
)
{
case
ValueType
::
boolean:
value
=
T
(
u_value
.
boolean
);
break
;
case
ValueType
::
integer:
value
=
T
(
u_value
.
integer
);
break
;
case
ValueType
::
floating:
value
=
T
(
u_value
.
floating_number
);
break
;
case
ValueType
::
string:
value
=
T
(
*
u_value
.
str
);
break
;
}
return
value
;
}
template
<
typename
T
>
OptionValue
CommandLineParser
::
CommandLineParserImpl
::
set_value
(
const
T
&
value
,
ValueType
value_type
)
{
OptionValue
u_value
;
switch
(
value_type
)
{
case
ValueType
::
boolean:
u_value
.
boolean
=
bool
(
value
);
break
;
case
ValueType
::
integer:
u_value
.
integer
=
int
(
value
);
break
;
case
ValueType
::
floating:
u_value
.
floating_number
=
double
(
value
);
break
;
case
ValueType
::
string:
m_option_string_values
.
emplace_back
(
std
::
to_string
(
value
));
u_value
.
str
=
&
m_option_string_values
.
back
();
break
;
}
return
u_value
;
}
template
<>
OptionValue
CommandLineParser
::
CommandLineParserImpl
::
set_value
(
const
std
::
string
&
value
,
ValueType
value_type
)
{
OptionValue
u_value
;
switch
(
value_type
)
{
case
ValueType
::
boolean:
if
(
value
==
"True"
or
value
==
"true"
or
value
==
"ON"
or
value
==
"on"
)
u_value
.
boolean
=
true
;
else
u_value
.
boolean
=
false
;
break
;
case
ValueType
::
integer:
u_value
.
integer
=
std
::
stoi
(
value
);
break
;
case
ValueType
::
floating:
u_value
.
floating_number
=
std
::
stod
(
value
);
break
;
case
ValueType
::
string:
m_option_string_values
.
emplace_back
(
value
);
u_value
.
str
=
&
m_option_string_values
.
back
();
break
;
}
return
u_value
;
}
template
<
typename
T
>
void
CommandLineParser
::
CommandLineParserImpl
::
set_option
(
BaseOption
&
opt
,
const
T
&
value
)
{
opt
.
value
=
set_value
<
T
>
(
value
,
opt
.
value_type
);
opt
.
is_set
=
true
;
}
// Add options and positional arguments
// ====================================
void
CommandLineParser
::
add_option
(
char
short_flag
,
const
std
::
string
&
long_flag
,
ValueType
value_type
,
const
std
::
string
&
help_message
)
{
m_impl
->
m_list_opt
.
emplace_back
(
short_flag
,
long_flag
,
value_type
,
help_message
);
}
void
CommandLineParser
::
add_option
(
char
short_flag
,
const
std
::
string
&
long_flag
,
int
default_value
,
const
std
::
string
&
help_message
)
{
m_impl
->
m_list_opt
.
emplace_back
(
short_flag
,
long_flag
,
ValueType
::
integer
,
m_impl
->
set_value
<
int
>
(
default_value
,
ValueType
::
integer
),
help_message
);
}
void
CommandLineParser
::
add_option
(
char
short_flag
,
const
std
::
string
&
long_flag
,
double
default_value
,
const
std
::
string
&
help_message
)
{
m_impl
->
m_list_opt
.
emplace_back
(
short_flag
,
long_flag
,
ValueType
::
floating
,
m_impl
->
set_value
<
double
>
(
default_value
,
ValueType
::
floating
),
help_message
);
}
void
CommandLineParser
::
add_option
(
char
short_flag
,
const
std
::
string
&
long_flag
,
bool
default_value
,
const
std
::
string
&
help_message
)
{
m_impl
->
m_list_opt
.
emplace_back
(
short_flag
,
long_flag
,
ValueType
::
boolean
,
m_impl
->
set_value
<
bool
>
(
default_value
,
ValueType
::
boolean
),
help_message
);
}
void
CommandLineParser
::
add_option
(
char
short_flag
,
const
std
::
string
&
long_flag
,
const
std
::
string
&
default_value
,
const
std
::
string
&
help_message
)
{
m_impl
->
m_list_opt
.
emplace_back
(
short_flag
,
long_flag
,
ValueType
::
string
,
m_impl
->
set_value
(
default_value
,
ValueType
::
string
),
help_message
);
}
void
CommandLineParser
::
add_pos_argument
(
const
std
::
string
&
name
,
ValueType
value_type
,
const
std
::
string
&
help_message
)
{
m_impl
->
m_list_args
.
emplace_back
(
name
,
value_type
,
help_message
);
}
// Parsing
// =======
int
CommandLineParser
::
parse
(
const
std
::
vector
<
std
::
string
>&
opts
)
{
add_option
(
'h'
,
"help"
,
false
,
"Print the help message"
);
m_impl
->
set_list_str_option
(
opts
);
if
(
get_option
<
ValueType
::
boolean
>
(
"help"
))
{
// if help we bypass
m_impl
->
print_help_message
();
return
1
;
}
else
{
m_impl
->
check_values
();
return
0
;
}
}
int
CommandLineParser
::
parse
(
int
argc
,
char
*
argv
[])
{
// register program name
register_program_name
(
std
::
string
(
argv
[
0
]));
// first build the list of options
std
::
vector
<
std
::
string
>
opts
;
opts
.
reserve
(
argc
);
for
(
int
i
=
1
;
i
<
argc
;
++
i
)
opts
.
emplace_back
(
std
::
string
(
argv
[
i
]));
// then parse them
return
parse
(
opts
);
}
// Usage
// =====
void
CommandLineParser
::
register_program_name
(
std
::
string
&&
name
)
{
m_impl
->
m_name
=
name
;
}
void
CommandLineParser
::
set_help_message
(
std
::
string
&&
help_msg
)
{
m_impl
->
m_help_msg
=
help_msg
;
}
void
CommandLineParser
::
CommandLineParserImpl
::
print_help_message
()
{
std
::
stringstream
msg
;
msg
<<
"Usage : "
<<
m_name
<<
" [Options]"
;
for
(
auto
&
arg:
m_list_args
)
{
msg
<<
" <"
+
arg
.
name
+
">"
;
}
msg
<<
"
\n\n
"
<<
m_help_msg
;
msg
<<
"
\n\n
Positional arguments :
\n
-----------------------
\n
"
;
for
(
auto
&
arg:
m_list_args
)
{
msg
<<
"
\t
"
<<
arg
.
name
<<
" : "
<<
arg
.
help_message
<<
"
\n
"
;
}
msg
<<
"
\n
Options :
\n
----------
\n
"
;
for
(
auto
&
opt:
m_list_opt
)
{
if
(
opt
.
value_type
==
ValueType
::
boolean
)
{
msg
<<
"
\t
-"
<<
opt
.
short_flag
<<
", --"
<<
opt
.
long_flag
<<
" : "
<<
opt
.
help_message
<<
"
\n
"
;
}
else
{
msg
<<
"
\t
-"
<<
opt
.
short_flag
<<
" <value>"
<<
", --"
<<
opt
.
long_flag
<<
"=<value>"
<<
" : "
<<
opt
.
help_message
<<
"
\n
"
;
}
}
std
::
cout
<<
msg
.
str
();
}
// Check values and options
// ========================
bool
CommandLineParser
::
CommandLineParserImpl
::
check_values
()
{
bool
retcode
=
check_pos_arguments
();
retcode
=
retcode
and
check_options
();
return
retcode
;
}
bool
CommandLineParser
::
CommandLineParserImpl
::
check_pos_arguments
()
{
size_t
expected
=
m_list_args
.
size
();
size_t
provided
=
0
;
for
(
auto
&
arg:
m_list_args
)
{
if
(
arg
.
is_set
)
++
provided
;
}
if
(
provided
!=
expected
)
{
throw
std
::
runtime_error
(
"Missing (a) positional argument(s) :"
"expected : "
+
std
::
to_string
(
expected
)
+
" , provided : "
+
std
::
to_string
(
provided
)
+
"."
);
}
return
true
;
}
bool
CommandLineParser
::
CommandLineParserImpl
::
check_options
()
{
for
(
auto
&
opt:
m_list_opt
)
{
if
((
opt
.
is_required
)
and
(
not
opt
.
is_set
))
{
throw
std
::
runtime_error
(
"Missing required option : '"
+
opt
.
long_flag
+
"'."
);
}
}
return
true
;
}
// Parse the list of argument
// ==========================
void
CommandLineParser
::
CommandLineParserImpl
::
set_list_str_option
(
const
std
::
vector
<
std
::
string
>&
options
)
{
if
(
options
.
size
()
<
1
)
return
;
m_list_opt
.
reserve
(
options
.
size
());
for
(
size_type
ind
=
0
;
ind
<
options
.
size
();)
{
// empty argument
std
::
string
opt
=
options
[
ind
];
if
(
opt
[
0
]
==
'\0'
)
{
++
ind
;
continue
;}
// positional argument
if
(
opt
[
0
]
!=
'-'
)
{
ind
=
analyse_pos_argument
(
ind
,
opt
);
continue
;
}
if
(
opt
[
1
]
!=
'-'
)
{
// short option
auto
sopts
=
split_short_options
(
opt
);
if
(
sopts
.
size
()
==
1
)
{
// only one option
ind
=
analyse_short_option
(
ind
,
options
,
sopts
[
0
]);
}
else
{
// several options
for
(
auto
it:
sopts
)
{
analyse_boolean_short_option
(
ind
,
it
);
}
++
ind
;
}
continue
;
}
else
{
// long option
ind
=
analyse_long_option
(
ind
,
options
,
opt
);
continue
;
}
}
}
size_t
CommandLineParser
::
CommandLineParserImpl
::
analyse_pos_argument
(
size_t
ind
,
const
std
::
string
&
arg_value
)
{
int
inda
=
next_free_pos_arg
();
if
(
inda
<
0
)
{
throw
std
::
runtime_error
(
"Too many positional arguments, expected : "
+
std
::
to_string
(
m_list_args
.
size
())
+
"."
);
}
auto
&
the_option
=
m_list_args
[
static_cast
<
size_type
>
(
inda
)];
set_option
(
the_option
,
arg_value
);
return
++
ind
;
}
size_t
CommandLineParser
::
CommandLineParserImpl
::
analyse_short_option
(
size_t
ind
,
const
raw_list_option
&
options
,
char
opt
)
{
int
option_index
=
get_short_option_index
(
opt
);
if
(
option_index
<
0
)
{
std
::
string
msg
=
"Error : unknown option '"
;
msg
.
push_back
(
opt
);
msg
+=
"'."
;
throw
std
::
runtime_error
(
msg
);
}
SwitchArgument
&
the_option
=
m_list_opt
[
option_index
];
ValueType
vtype
=
the_option
.
value_type
;
if
(
vtype
==
ValueType
::
boolean
)
{
set_option
(
the_option
,
true
);
++
ind
;
}
else
{
if
(
ind
>
0
and
ind
>=
options
.
size
()
-
1
)
{
throw
std
::
runtime_error
(
"Missing an argument for option : "
+
the_option
.
long_flag
);
}
std
::
string
next_value
=
std
::
string
(
options
[
ind
+
1
]);
if
(
not
is_value
(
next_value
))
{
throw
std
::
runtime_error
(
"Missing an argument for option : "
+
the_option
.
long_flag
);
}
ind
+=
2
;
set_option
(
the_option
,
next_value
);
}
return
ind
;
}
size_t
CommandLineParser
::
CommandLineParserImpl
::
analyse_long_option
(
size_t
ind
,
const
raw_list_option
&
option
,
const
std
::
string
&
opt
)
{
option_value_t
pair
=
split_long_option
(
opt
);
int
option_index
=
get_long_option_index
(
pair
.
first
);
SwitchArgument
&
the_option
=
m_list_opt
[
option_index
];
ValueType
vtype
=
the_option
.
value_type
;
if
(
pair
.
second
.
size
()
>
0
)
{
set_option
(
the_option
,
pair
.
second
);
++
ind
;
return
ind
;
}
if
(
vtype
==
ValueType
::
boolean
)
{
if
(
pair
.
second
.
size
()
>
0
)
set_option
(
the_option
,
pair
.
second
);
else
set_option
(
the_option
,
true
);
++
ind
;
}
else
{
if
(
ind
>=
option
.
size
())
{
throw
std
::
runtime_error
(
"Missing an argument for option : "
+
the_option
.
long_flag
);
}
std
::
string
next_value
=
std
::
string
(
option
[
ind
+
1
]);
if
(
not
is_value
(
next_value
))
{
throw
std
::
runtime_error
(
"Missing an argument for option : "
+
the_option
.
long_flag
);
}
set_option
(
the_option
,
next_value
);
ind
=
ind
+
2
;
}
return
ind
;
}
size_t
CommandLineParser
::
CommandLineParserImpl
::
analyse_boolean_short_option
(
size_t
ind
,
char
option
)
{
int
option_index
=
get_short_option_index
(
option
);
if
(
option_index
<
0
)
{
std
::
string
msg
=
"Error : unknown option '"
;
msg
.
push_back
(
option
);
msg
+=
"'."
;
throw
std
::
runtime_error
(
msg
);
}
SwitchArgument
&
the_option
=
m_list_opt
[
option_index
];
ValueType
vtype
=
the_option
.
value_type
;
if
(
vtype
!=
ValueType
::
boolean
)
{
throw
std
::
runtime_error
(
"Error, expected boolean for option "
+
the_option
.
long_flag
);
}
set_option
(
the_option
,
true
);
return
ind
;
}
// Indexes of options
// ==================
int
CommandLineParser
::
CommandLineParserImpl
::
get_short_option_index
(
char
flag
)
noexcept
{
int
ind
=
-
1
;
for
(
auto
it
=
m_list_opt
.
begin
();
it
!=
m_list_opt
.
end
();
++
it
)
{
if
(
it
->
short_flag
==
flag
)
{
ind
=
it
-
m_list_opt
.
begin
();
break
;
}
}
return
ind
;
}
int
CommandLineParser
::
CommandLineParserImpl
::
get_long_option_index
(
const
std
::
string
&
flag
)
noexcept
{
int
ind
=
-
1
;
for
(
auto
it
=
m_list_opt
.
begin
();
it
!=
m_list_opt
.
end
();
++
it
)
{
if
(
it
->
long_flag
==
flag
)
{
ind
=
it
-
m_list_opt
.
begin
();
break
;
}
}
return
ind
;
}
size_t
CommandLineParser
::
CommandLineParserImpl
::
get_safe_long_option_index
(
const
std
::
string
&
long_flag
)
{
int
ind
=
get_long_option_index
(
long_flag
);
if
(
ind
==
-
1
)
{
throw
std
::
runtime_error
(
"No such option : "
+
long_flag
);
}
return
static_cast
<
size_type
>
(
ind
);
}
int
CommandLineParser
::
CommandLineParserImpl
::
next_free_pos_arg
()
noexcept
{
int
ind
=
-
1
;
for
(
auto
it
=
m_list_args
.
begin
();
it
!=
m_list_args
.
end
();
++
it
)
{
if
(
!
(
*
it
).
is_set
)
{
ind
=
it
-
m_list_args
.
begin
();
break
;
}
}
return
ind
;
}
size_t
CommandLineParser
::
CommandLineParserImpl
::
get_safe_pos_argument_index
(
const
std
::
string
&
name
)
{
int
ind
=
-
1
;
for
(
auto
it
=
m_list_args
.
begin
();
it
!=
m_list_args
.
end
();
++
it
)
{
if
(
name
==
(
*
it
).
name
)
{
ind
=
it
-
m_list_args
.
begin
();
break
;
}
}
if
(
ind
<
0
)
{
throw
std
::
runtime_error
(
"Unknow positional argument : "
+
name
);
}
return
static_cast
<
size_t
>
(
ind
);
}
// Split options
// =============
// remove '-' for short and '--' for long options
// also split long options if they are in the form 'opt=value'
option_value_t
split_long_option
(
const
std
::
string
&
opts
)
{
option_value_t
val
;
auto
it
=
std
::
find
(
opts
.
begin
(),
opts
.
end
(),
'='
);
// no equal sign
if
(
it
==
opts
.
end
())
{
val
.
first
=
opts
.
substr
(
2
,
std
::
string
::
npos
);
}
// equal sign
else
{
std
::
string
::
size_type
len_before
=
it
-
opts
.
begin
();
val
.
first
=
opts
.
substr
(
2
,
len_before
-
2
);
if
(
opts
.
size
()
>
len_before
)
{
// if there is something after
val
.
second
=
opts
.
substr
(
len_before
+
1
,
std
::
string
::
npos
);
}
}
return
val
;
}
std
::
vector
<
char
>
split_short_options
(
const
std
::
string
&
opts
)
{
std
::
vector
<
char
>
shortopts
;
shortopts
.
reserve
(
opts
.
size
()
-
1
);
for
(
size_t
i
=
1
;
i
<
opts
.
size
();
++
i
)
{
shortopts
.
emplace_back
(
opts
[
i
]);
}
return
shortopts
;
}
}
//end namespace cli
}
//end namespace specmicp
Event Timeline
Log In to Comment