summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/opresolv.h
blob: 52030dc43365f7a6c09170604d67f595bb13561d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/****************************************************************************

    opresolv.h

    Extensible ranged option resolution handling

    An extensible mechanism for handling options is a major need in MESS and
    Imgtool.  Unfortunately, since we are using straight C for everything, it
    can be hard and awkward to create non-arcane mechanisms for representing
    these options.

    In this system, we have the following concepts:
    1.  An "option specification"; a string that represents what options are
        available, their defaults, and their allowed ranges.  Here is an
        example:

        Examples:
            "H[1]-2;T[35]/40/80;S[18]"
                Allow 1-2 heads; 35, 40 or 80 tracks, and 18 sectors,
                defaulting to 1 heads and 35 tracks.

            "N'Simon''s desk'"
                Simon's desk (strings are not subject to range checking)

    2.  An "option guide"; a struct that provides information about what the
        various members of the option specification mean (i.e. - H=heads)

    3.  An "option resolution"; an object that represents a set of interpreted
        options.  At this stage, the option bid has been processed and it is
        guaranteed that all options reside in their expected ranges.

    An option_resolution object is created based on an option guide and an
    option specification.  It is then possible to specify individual parameters
    to the option_resolution object.  Argument checks occur at this time.  When
    one is all done, you can then query the object for any given value.

****************************************************************************/

#ifndef MAME_UTIL_OPRESOLV_H
#define MAME_UTIL_OPRESOLV_H

#pragma once

#include <stdlib.h>
#include <vector>
#include <string>

#include "coretmpl.h"


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

#define OPTION_GUIDE_START(option_guide_)                                                       \
	namespace option_guide_impl_##option_guide_                                                 \
	{                                                                                           \
		static const util::option_guide &get();                                                 \
	};                                                                                          \
	const util::option_guide &option_guide_ = option_guide_impl_##option_guide_::get();         \
	namespace option_guide_impl_##option_guide_                                                 \
	{                                                                                           \
		static const auto actual = util::make_option_guide(0
#define OPTION_GUIDE_END                                                                        \
		);                                                                                      \
		static const util::option_guide &get() { return actual; }                               \
	};
#define OPTION_GUIDE_EXTERN(option_guide_)                                                      \
	extern const util::option_guide &option_guide_
#define OPTION_INT(option_char, identifier, display_name)                                       \
	,util::option_guide::entry(util::option_guide::entry::option_type::INT, (option_char), (identifier), (display_name))
#define OPTION_STRING(option_char, identifier, display_name)                                    \
	,util::option_guide::entry(util::option_guide::entry::option_type::STRING, (option_char), (identifier), (display_name))
#define OPTION_ENUM_START(option_char, identifier, display_name)                                \
	,util::option_guide::entry(util::option_guide::entry::option_type::ENUM_BEGIN, (option_char), (identifier), (display_name))
#define OPTION_ENUM(value, identifier, display_name)                                            \
	,util::option_guide::entry(util::option_guide::entry::option_type::ENUM_VALUE, (value), (identifier), (display_name))
#define OPTION_ENUM_END

namespace util {

// ======================> option_guide

class option_guide
{
public:
	class entry
	{
	public:
		enum class option_type
		{
			INVALID,
			INT,
			STRING,
			ENUM_BEGIN,
			ENUM_VALUE
		};

		constexpr entry()
			: m_type(option_type::INVALID), m_parameter(0), m_identifier(nullptr), m_display_name(nullptr)
		{
		}

		constexpr entry(option_type type, int parameter = 0, const char *identifier = nullptr, const char *display_name = nullptr)
			: m_type(type), m_parameter(parameter), m_identifier(identifier), m_display_name(display_name)
		{
		}

		// accessors
		const option_type type() const { return m_type; }
		int parameter() const { return m_parameter; }
		const char *identifier() const { return m_identifier; }
		const char *display_name() const { return m_display_name; }

	private:
		option_type m_type;
		int         m_parameter;
		const char *m_identifier;
		const char *m_display_name;
	};

	// methods
	const util::contiguous_sequence_wrapper<const entry> &entries() const { return m_entries; }

protected:
	option_guide(const entry *begin, size_t count)
		: m_entries(begin, count)
	{
	}


private:
	util::contiguous_sequence_wrapper<const entry> m_entries;
};

// ======================> option_guide_impl

template <std::size_t Count>
class option_guide_impl : protected std::array<option_guide::entry, Count>, public option_guide
{
public:
	template<typename... T>
	option_guide_impl(T &&... elems)
		: std::array<option_guide::entry, Count>({ std::forward<T>(elems)... })
		, option_guide(Count > 0 ? &(*this)[0] : nullptr, Count)
	{
	}
};

template <typename... T>
option_guide_impl<sizeof...(T)> make_option_guide(int dummy, T &&... elems)
{
	return option_guide_impl<sizeof...(T)>(std::forward<T>(elems)...);
}


// ======================> option_resolution

class option_resolution
{
public:
	// TODO - audit unused parameters
	enum class error
	{
		SUCCESS,
		OUTOFMEMORY,
		PARAMOUTOFRANGE,
		PARAMNOTSPECIFIED,
		PARAMNOTFOUND,
		PARAMALREADYSPECIFIED,
		BADPARAM,
		SYNTAX,
		INTERNAL
	};

	template<typename T>
	struct range
	{
		range() { min = -1; max = -1; }
		T min, max;
	};

	// class to represent specific entry
	class entry
	{
		friend class option_resolution;

	public:
		typedef std::vector<range<int> > rangelist;

		// ctor
		entry(const option_guide::entry &guide_entry);

		// returns true if this entry is subject to range constraints
		bool is_ranged() const { return !m_ranges.empty(); }

		// accessors
		bool is_pertinent() const { return m_is_pertinent; }
		const std::string &value() const;
		int value_int() const;
		const std::string &default_value() const { return m_default_value; }
		const rangelist &ranges() const { return m_ranges; }
		const option_guide::entry *enum_value_begin() const { return m_enum_value_begin; }
		const option_guide::entry *enum_value_end() const { return m_enum_value_end; }

		// accessors for guide data
		option_guide::entry::option_type option_type() const { return m_guide_entry.type(); }
		const char *display_name() const { return m_guide_entry.display_name(); }
		const char *identifier() const { return m_guide_entry.identifier(); }
		int parameter() const { return m_guide_entry.parameter(); }


		// mutators
		bool set_value(const std::string &value);
		bool set_value(int value) { return set_value(numeric_value(value)); }

		// higher level operations
		bool can_bump_lower() const;
		bool can_bump_higher() const;
		bool bump_lower();
		bool bump_higher();

	private:
		// references to the option guide
		const option_guide::entry &             m_guide_entry;
		const option_guide::entry *             m_enum_value_begin;
		const option_guide::entry *             m_enum_value_end;

		// runtime state
		bool                                    m_is_pertinent;
		std::string                             m_value;
		std::string                             m_default_value;
		rangelist                               m_ranges;

		// methods
		void parse_specification(const char *specification);
		void set_enum_value_range(const option_guide::entry *begin, const option_guide::entry *end);
		static std::string numeric_value(int value);
		rangelist::const_iterator find_in_ranges(int value) const;
	};

	// ctor/dtor
	option_resolution(const option_guide &guide);
	~option_resolution();

	// sets a specification
	void set_specification(const std::string &specification);

	// entry lookup - note that we're not exposing m_entries directly because we don't really
	// have a way of making the list be immutable but the members be mutable at the same time
	std::vector<entry>::iterator entries_begin() { return m_entries.begin(); }
	std::vector<entry>::iterator entries_end() { return m_entries.end(); }
	entry *find(int parameter);
	entry *find(const std::string &identifier);
	int lookup_int(int parameter);
	const std::string &lookup_string(int parameter);

	// misc
	static const char *error_string(error err);
	static error get_default(const char *specification, int option_char, int *val);

private:
	std::vector<entry> m_entries;

	error set_parameter(std::vector<entry>::iterator iter, const std::string &value);

	static const char *lookup_in_specification(const char *specification, const option_guide::entry &option);
	const entry *lookup_entry(int option_char) const;
};


} // namespace util

#endif // MAME_UTIL_OPRESOLV_H