mpd: fix floating point exception when adding random tags/songs
[ncmpcpp.git] / src / utility / option_parser.h
blob6b366bd91a06e937120ef8403dd5ab05d714c79a
1 /*
2 * Copyright (c) 2014, Andrzej Rybczak <electricityispower@gmail.com>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #ifndef NCMPCPP_UTILITY_OPTION_PARSER_H
34 #define NCMPCPP_UTILITY_OPTION_PARSER_H
36 #include <boost/lexical_cast.hpp>
37 #include <cassert>
38 #include <stdexcept>
39 #include <unordered_map>
41 #include "utility/functional.h"
43 struct option_parser
45 typedef std::function<void(std::string &&)> parser_t;
46 typedef std::function<void()> default_t;
48 template <typename DestT, typename SourceT>
49 struct assign_value_once
51 typedef DestT dest_type;
52 typedef typename std::decay<SourceT>::type source_type;
54 template <typename ArgT>
55 assign_value_once(DestT &dest, ArgT &&value)
56 : m_dest(dest), m_source(std::make_shared<source_type>(std::forward<ArgT>(value))) { }
58 void operator()()
60 assert(m_source.get() != nullptr);
61 m_dest = std::move(*m_source);
62 m_source.reset();
65 private:
66 DestT &m_dest;
67 std::shared_ptr<source_type> m_source;
70 template <typename IntermediateT, typename DestT, typename TransformT>
71 struct parse_and_transform
73 template <typename ArgT>
74 parse_and_transform(DestT &dest, ArgT &&map)
75 : m_dest(dest), m_map(std::forward<ArgT>(map)) { }
77 void operator()(std::string &&v)
79 try {
80 m_dest = m_map(boost::lexical_cast<IntermediateT>(v));
81 } catch (boost::bad_lexical_cast &) {
82 throw std::runtime_error("invalid value: " + v);
86 private:
87 DestT &m_dest;
88 TransformT m_map;
91 struct worker
93 worker() { }
95 template <typename ParserT, typename DefaultT>
96 worker(ParserT &&p, DefaultT &&d)
97 : m_defined(false), m_parser(std::forward<ParserT>(p))
98 , m_default(std::forward<DefaultT>(d)) { }
100 template <typename ValueT>
101 void parse(ValueT &&value)
103 if (m_defined)
104 throw std::runtime_error("option already defined");
105 m_parser(std::forward<ValueT>(value));
106 m_defined = true;
109 bool defined() const { return m_defined; }
110 void run_default() const { m_default(); }
112 private:
113 bool m_defined;
114 parser_t m_parser;
115 default_t m_default;
118 template <typename ParserT, typename DefaultT>
119 void add(const std::string &option, ParserT &&p, DefaultT &&d)
121 assert(m_parsers.count(option) == 0);
122 m_parsers[option] = worker(std::forward<ParserT>(p), std::forward<DefaultT>(d));
125 template <typename WorkerT>
126 void add(const std::string &option, WorkerT &&w)
128 assert(m_parsers.count(option) == 0);
129 m_parsers[option] = std::forward<WorkerT>(w);
132 bool run(std::istream &is);
134 private:
135 std::unordered_map<std::string, worker> m_parsers;
138 template <typename IntermediateT, typename ArgT, typename TransformT>
139 option_parser::parser_t assign(ArgT &arg, TransformT &&map = id_())
141 return option_parser::parse_and_transform<IntermediateT, ArgT, TransformT>(
142 arg, std::forward<TransformT>(map)
146 template <typename ArgT, typename ValueT>
147 option_parser::default_t defaults_to(ArgT &arg, ValueT &&value)
149 return option_parser::assign_value_once<ArgT, ValueT>(
150 arg, std::forward<ValueT>(value)
154 template <typename IntermediateT, typename ArgT, typename ValueT, typename TransformT>
155 option_parser::worker assign_default(ArgT &arg, ValueT &&value, TransformT &&map)
157 return option_parser::worker(
158 assign<IntermediateT>(arg, std::forward<TransformT>(map)),
159 defaults_to(arg, map(std::forward<ValueT>(value)))
163 template <typename ArgT, typename ValueT>
164 option_parser::worker assign_default(ArgT &arg, ValueT &&value)
166 return assign_default<ArgT>(arg, std::forward<ValueT>(value), id_());
169 // workers for specific types
171 option_parser::worker yes_no(bool &arg, bool value);
173 #endif // NCMPCPP_UTILITY_OPTION_PARSER_H