cmdopt: add simple reimplementation of libcmdopt

This commit is contained in:
Danny Robson 2015-06-10 21:28:52 +10:00
parent 1d65a7ed77
commit 250b8d21ec
5 changed files with 446 additions and 13 deletions

View File

@ -16,6 +16,9 @@ UTIL_FILES = \
bezier.hpp \ bezier.hpp \
bitwise.cpp \ bitwise.cpp \
bitwise.hpp \ bitwise.hpp \
cmdopt.cpp \
cmdopt.hpp \
cmdopt.ipp \
colour.cpp \ colour.cpp \
colour.hpp \ colour.hpp \
colour.ipp \ colour.ipp \

263
cmdopt.cpp Normal file
View File

@ -0,0 +1,263 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#include "cmdopt.hpp"
#include "debug.hpp"
#include <algorithm>
#include <cstring>
#include <sstream>
using util::cmdopt::option::base;
using util::cmdopt::option::bytes;
using util::cmdopt::option::count;
using util::cmdopt::option::value;
using util::cmdopt::parser;
///////////////////////////////////////////////////////////////////////////////
base::base (std::string _name):
m_name (_name)
{ ; }
//-----------------------------------------------------------------------------
base::~base ()
{ ; }
//-----------------------------------------------------------------------------
void
base::execute (void)
{
throw invalid_null (m_name);
}
//-----------------------------------------------------------------------------
void
base::execute (const char *restrict)
{
throw invalid_value (m_name);
}
//-----------------------------------------------------------------------------
void
base::start (void)
{ ; }
//-----------------------------------------------------------------------------
void
base::finish (void)
{ ; }
//-----------------------------------------------------------------------------
std::string
base::name (void) const
{
return m_name;
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
value<T>::value (std::string _name, T &_data):
base (_name),
m_data (_data)
{ ; }
//-----------------------------------------------------------------------------
template <typename T>
void
value<T>::execute (const char *restrict str)
{
try {
std::istringstream os (str);
os.exceptions (
std::istringstream::failbit
| std::istringstream::badbit
);
os >> m_data;
if (!os.eof ())
throw invalid_value (__func__);
} catch (...) {
throw invalid_value (__func__);
}
}
//-----------------------------------------------------------------------------
template <typename T>
T
value<T>::data (void) const
{
return m_data;
}
//-----------------------------------------------------------------------------
template <typename T>
T&
value<T>::data (void)
{
return m_data;
}
//-----------------------------------------------------------------------------
namespace util { namespace cmdopt { namespace option {
template class value<uint16_t>;
template class value<uint32_t>;
template class value<uint64_t>;
} } }
///////////////////////////////////////////////////////////////////////////////
template <typename T>
void
count<T>::execute (void)
{
++ this->data();
}
//-----------------------------------------------------------------------------
namespace util { namespace cmdopt { namespace option {
template class count<unsigned>;
} } }
///////////////////////////////////////////////////////////////////////////////
int
parser::scan (int argc, char *const *argv)
{
CHECK_GE (argc, 0);
CHECK (argv);
// start iterating after our program's name
int i = 1;
while (i < argc) {
auto len = strlen (argv[i]);
// bail if there's no potential for an option
if (len < 2 || argv[i][0] != '-')
return i;
// parse longopt
auto inc = argv[i][1] == '-'
? parse_long (i, argc, argv)
: parse_short (i, argc, argv);
CHECK_GT (inc, 0);
i += inc;
}
return i;
}
//-----------------------------------------------------------------------------
int
parser::parse_long (int pos, int argc, char *const *argv)
{
CHECK_LT (pos, argc);
CHECK_GE (pos, 0);
CHECK_GE (argc, 0);
CHECK (argv);
CHECK_EQ (argv[pos][0], '-');
CHECK_EQ (argv[pos][1], '-');
// break first atom into components and extract the key
const char *start = argv[pos] + 2;
const char *eq = strchr (start, '=');
const char *last = start + strlen (start);
std::string key { start, eq ? eq : last };
// find the handler
auto handle_pos = std::find_if (m_long.begin (),
m_long.end (),
[&] (auto i) { return std::get<0> (i) == key; });
if (handle_pos == m_long.end ())
throw invalid_key (key);
auto &handler = std::get<1> (*handle_pos);
// maybe grab a value from the next atom and dispatch
if (!eq) {
// check the next atom for the value
if (pos + 1 < argc)
if (argv[pos + 1][0] != '-') {
handler.execute (argv[pos+1]);
return 2;
}
handler.execute ();
} else {
handler.execute (eq+1);
}
return 1;
}
//-----------------------------------------------------------------------------
int
parser::parse_short (int pos, int argc, char *const *argv)
{
CHECK_LT (pos, argc);
CHECK_GE (pos, 0);
CHECK_GE (argc, 0);
CHECK (argv);
CHECK_EQ (argv[pos][0], '-');
CHECK_NEQ (argv[pos][1], '-');
// we have a run of no-value keys
auto len = strlen (argv[pos]);
if (len > 2 || pos + 1 == argc || argv[pos+1][0] == '-') {
for (size_t i = 1; i < len; ++i) {
auto letter = argv[pos][i];
auto hpos = std::find_if (m_short.begin (),
m_short.end (),
[letter] (auto j) { return std::get<0> (j) == letter; });
if (hpos == m_short.end ())
throw invalid_key (std::to_string (letter));
std::get<1> (*hpos).execute ();
}
return 1;
}
// we have a value following
auto letter = argv[pos][1];
auto hpos = std::find_if (m_short.begin (),
m_short.end (),
[letter] (auto i) { return std::get<0> (i) == letter; });
if (hpos == m_short.end ())
throw invalid_key (std::to_string (letter));
std::get<1> (*hpos).execute (argv[pos+1]);
return 2;
}

117
cmdopt.hpp Normal file
View File

@ -0,0 +1,117 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_CMDLINE_HPP
#define __UTIL_CMDLINE_HPP
#include <memory>
#include <tuple>
#include <vector>
#include <stdexcept>
#include <string>
namespace util { namespace cmdopt {
namespace option {
class base {
public:
base (std::string name);
virtual ~base ();
virtual void execute (void);
virtual void execute (const char *restrict);
virtual void start (void);
virtual void finish (void);
std::string name (void) const;
private:
std::string m_name;
};
template <typename T>
class value : public base {
public:
value (std::string name, T&);
void execute (const char *restrict) override;
T data (void) const;
T& data (void);
private:
T& m_data;
};
template <typename T = unsigned>
class count : public value<T> {
public:
count (std::string name);
void execute (void) override;
};
class bytes : public value<size_t> {
public:
bytes (std::string name);
void execute (const char *restrict) override;
};
}
class error : public std::runtime_error
{ using runtime_error::runtime_error; };
class invalid_key : public error
{ using error::error; };
class invalid_value : public error
{ using error::error; };
class invalid_null : public error
{ using error::error; };
class parser {
public:
template <typename T, typename ...Args>
T& add (char shortname,
std::string longname,
std::string description,
Args&&...);
int scan (int argc, char *const *argv);
private:
int parse_long (int pos, int argc, char *const *argv);
int parse_short (int pos, int argc, char *const *argv);
using short_t = std::tuple<char,option::base&>;
using long_t = std::tuple<std::string,option::base&>;
std::vector<short_t> m_short;
std::vector<long_t> m_long;
std::vector<std::unique_ptr<option::base>> m_options;
};
} }
#include "cmdopt.ipp"
#endif

37
cmdopt.ipp Normal file
View File

@ -0,0 +1,37 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#ifdef __UTIL_CMDLINE_IPP
#error
#endif
#define __UTIL_CMDLINE_IPP
namespace util { namespace cmdopt {
template <typename T, typename ...Args>
T&
parser::add (char shortname, std::string longname, std::string description, Args&&... args)
{
auto handler = std::make_unique<T> (longname, std::forward<Args> (args)...);
T& ref = *handler;
m_short.emplace_back (shortname, ref);
m_long .emplace_back (longname, ref);
m_options.emplace_back (std::move (handler));
return ref;
}
} }

View File

@ -270,20 +270,33 @@ adjust_ocean (util::image::buffer<float> &height,
static const unsigned THERMAL_ITERATIONS = 10; static const unsigned THERMAL_ITERATIONS = 10;
static const unsigned HYDRAULIC_ITERATIONS = 100; static const unsigned HYDRAULIC_ITERATIONS = 100;
#include "cmdopt.hpp"
int int
main (void) main (int argc, char **argv)
{ {
// setup the output buffer // setup default variables
#ifdef ENABLE_DEBUGGING #ifdef ENABLE_DEBUGGING
util::extent2u size {320, 240}; util::extent2u res {320, 240};
#else #else
util::extent2u size {1920, 1080}; util::extent2u res {1920, 1080};
#endif #endif
util::image::buffer<float> img (size);
uint64_t seed = time (nullptr); uint64_t seed = time (nullptr);
// fill variables from arguments
util::cmdopt::parser args;
args.add<util::cmdopt::option::value<size_t>> ('w', "width", "output image width", res.w);
args.add<util::cmdopt::option::value<size_t>> ('h', "height", "output image height", res.h);
args.add<util::cmdopt::option::value<uint64_t>> ('s', "seed", "random seed", seed);
//args.add<util::cmdopt::option::value<unsigned>> ('o', "octaves", "total fractal iterations", octaves);
//args.add<util::cmdopt::option::value<float>> ('H', "hurst", "Hurst exponent", H);
args.scan (argc, argv);
util::image::buffer<float> img (res);
// setup the noise generator // setup the noise generator
#if 0 #if 0
//util::noise::fractal::fbm<float, util::noise::basis::worley<float>> b (seed); //util::noise::fractal::fbm<float, util::noise::basis::worley<float>> b (seed);
@ -294,7 +307,7 @@ main (void)
util::noise::fractal::hetero<float, util::noise::basis::value<float,util::lerp::quintic>> b (seed); util::noise::fractal::hetero<float, util::noise::basis::value<float,util::lerp::quintic>> b (seed);
b.octaves (8); b.octaves (8);
b.frequency (10.f / size.w); b.frequency (10.f / res.w);
b.lacunarity = 2.f; b.lacunarity = 2.f;
b.H = 1.0f; b.H = 1.0f;
b.seed (seed); b.seed (seed);
@ -306,21 +319,21 @@ main (void)
util::noise::fractal::fbm<float, util::noise::basis::perlin<float,util::lerp::quintic>> util::noise::fractal::fbm<float, util::noise::basis::perlin<float,util::lerp::quintic>>
> b (seed, { 0.13f, 0.13f }); > b (seed, { 0.13f, 0.13f });
b.data.frequency (1.f / size.w); b.data.frequency (1.f / res.w);
b.perturb[0].octaves (4); b.perturb[0].octaves (4);
b.perturb[1].octaves (4); b.perturb[1].octaves (4);
b.perturb[0].frequency (10.f / size.w); b.perturb[0].frequency (10.f / res.w);
b.perturb[1].frequency (10.f / size.w); b.perturb[1].frequency (10.f / res.w);
#endif #endif
// generate the values. offset positions slightly to observe simple axis issues with perlin basis // generate the values. offset positions slightly to observe simple axis issues with perlin basis
{ {
auto offset = util::vector2f { -100 }; auto offset = util::vector2f { -100 };
for (size_t y = 0; y < size.h; ++y) for (size_t y = 0; y < res.h; ++y)
for (size_t x = 0; x < size.w; ++x) { for (size_t x = 0; x < res.w; ++x) {
auto v = b (util::point2f {float (x), float (y)} + offset); auto v = b (util::point2f {float (x), float (y)} + offset);
img.data ()[y * size.w + x] = v; img.data ()[y * res.w + x] = v;
} }
} }
@ -342,7 +355,7 @@ main (void)
std::cerr << "thermal_erosion\n"; std::cerr << "thermal_erosion\n";
for (size_t i = 0; i < THERMAL_ITERATIONS; ++i) for (size_t i = 0; i < THERMAL_ITERATIONS; ++i)
thermal_erode (soft, to_radians (30.f), 1.f / size.w, 0.f); thermal_erode (soft, to_radians (30.f), 1.f / res.w, 0.f);
hydraulic_erode (soft, HYDRAULIC_ITERATIONS); hydraulic_erode (soft, HYDRAULIC_ITERATIONS);