Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/CommandLine.h @ 1784

Last change on this file since 1784 was 1764, checked in by rgrieder, 16 years ago

Moved Exception to util now that debug has been moved.

  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#ifndef _CommandLine_H__
30#define _CommandLine_H__
31
32#include "CorePrereqs.h"
33#include <map>
34#include "util/Convert.h"
35#include "util/Debug.h"
36#include "util/Exception.h"
37
38#define SetCommandLineArgument(name, defaultValue) \
39    BaseCommandLineArgument& CmdArgumentDummyBoolVar##name \
40    = orxonox::CommandLine::addArgument(#name, defaultValue)
41#define SetCommandLineSwitch(name) \
42    BaseCommandLineArgument& CmdArgumentDummyBoolVar##name \
43    = orxonox::CommandLine::addArgument(#name, false)
44
45
46namespace orxonox
47{
48    /**
49    @brief
50        Template struct which creates a distinct type for every integer constant.
51    @note
52        This allows to select a different function depending on a boolean value
53        when creating a new CommandLineArgument.
54        From 'Modern C++ Design' (Alexandrescu 2001).
55    */
56    template <int v>
57    struct Int2Type
58    {
59        enum { value = v };
60    };
61
62    /**
63    @brief
64        Base class for CommandLineArguments.
65    */
66    class _CoreExport BaseCommandLineArgument
67    {
68        friend class CommandLine;
69
70    public:
71        //! Tells whether the value has been changed by the command line.
72        bool hasDefaultValue() const { return bHasDefaultValue_; }
73        //! Returns the name of the argument.
74        const std::string& getName() const { return name_; }
75
76        //! Returns the shortcut (example: "-p 22" for "--port 22") of the argument.
77        //! Evaluates to "" if none there is none.
78        const std::string& getShortcut() const { return shortcut_; }
79        //! Sets the shortcut for the argument
80        BaseCommandLineArgument& setShortcut(const std::string& shortcut)
81        { this->shortcut_ = shortcut; return *this; }
82
83        //! Returns the usage information
84        const std::string& getInformation() const { return this->usageInformation_; }
85        //! Sets the option information when displaying orxonox usage.
86        BaseCommandLineArgument& setInformation(const std::string& usage)
87        { this->usageInformation_ = usage; return *this; }
88
89    protected:
90        BaseCommandLineArgument(const std::string& name)
91            : bHasDefaultValue_(true)
92            , name_(name)
93        { }
94
95        //! Undefined copy constructor
96        BaseCommandLineArgument(const BaseCommandLineArgument& instance);
97        virtual ~BaseCommandLineArgument() { }
98
99        //! Parses the value string of a command line argument.
100        virtual void parse(const std::string& value) = 0;
101
102        //! Tells whether the value has been changed by the command line.
103        bool bHasDefaultValue_;
104
105    private:
106        std::string name_;             //!< Name of the argument
107        std::string shortcut_;         //!< Shortcut of the argument. @see getShortcut().
108        std::string usageInformation_; //!< Tells about the usage of this parameter
109    };
110
111
112    /**
113    @brief
114        Container class for a command line argument of type T.
115       
116        Whenever you want to have an option specified by a command line switch,
117        you need to first define it with SetCommandLineArgument(name, defaultValue).
118        It is then added to a map and possibly changed when the command line is being parsed.
119        If the option was not given, you can detect this by asking hasDefaultValue().
120       
121        There is a possibility to define a short cut so you can write "-p 20" instead of "--port 20".
122        Note the difference between "-" and "--"!
123        Also, there is no restriction to the number of strings you add after --name.
124        So "--startVector (2, 4, 5)" is perfectly legal.
125
126        Retrieving an argument is possible with the getCommandLineArgument function of the
127        CommandLine class. It is a Singleton, but the public interface is static.
128    */
129    template <class T>
130    class CommandLineArgument : public BaseCommandLineArgument
131    {
132        // Only let CommandLine change the value.
133        friend class CommandLine;
134
135    public:
136        //! Returns the actual value of the argument. Can be equal to default value.
137        T getValue() const { return value_; }
138        //! Returns the given default value as type T.
139        T getDefaultValue() const { return defaultValue_; }
140
141    private:
142        //! Constructor initialises both value_ and defaultValue_ with defaultValue.
143        CommandLineArgument(const std::string& name, const T& defaultValue)
144            : BaseCommandLineArgument(name)
145            , value_(defaultValue)
146            , defaultValue_(defaultValue)
147        { }
148
149        virtual void parse(const std::string& value);
150
151        T value_;            //!< The actual value
152        T defaultValue_;     //!< Default value. Should not be changed.
153    };
154
155    /**
156    @brief
157        Parses a value string for a command line argument.
158        It simply uses convertValue(Output, Input) to do that.
159    */
160    template <class T>
161    void CommandLineArgument<T>::parse(const std::string& value)
162    {
163        if (convertValue(&this->value_, value))
164        {
165            this->bHasDefaultValue_ = false;
166        }
167        else
168        {
169            ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
170        }
171    }
172
173    /**
174    @brief
175        Parses a value string for a command line argument.
176        It simply uses convertValue(Output, Input) to do that.
177        This is a template specialisation for bool type. That is necessary
178        so that you can have simple command line switches.
179    */
180    template <>
181    inline void CommandLineArgument<bool>::parse(const std::string& value)
182    {
183        if (convertValue(&this->value_, value))
184        {
185            this->bHasDefaultValue_ = false;
186        }
187        else if (value == "")
188        {
189            this->bHasDefaultValue_ = false;
190            this->value_ = true;
191        }
192        else
193        {
194            ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
195        }
196    }
197
198
199    /**
200    @brief
201        Global interface to command line options.
202        Allows to add and retrieve command line arguments. Also does the parsing.
203    @note
204        Internally it is a Singleton, but the public interface is static.
205    @see
206        CommandLineArgument
207    */
208    class _CoreExport CommandLine
209    {
210    public:
211
212        //! Parse redirection to internal member method.
213        static void parse(const std::vector<std::string>& arguments) { _getInstance()._parse(arguments); }
214
215        static std::string getUsageInformation();
216
217        template <class T>
218        static const CommandLineArgument<T>* getArgument(const std::string& name);
219        //! Writes the argument value in the given parameter.
220        template <class T>
221        static void getValue(const std::string& name, T* value)
222        { *value = getArgument<T>(name)->getValue(); }
223        template <class T>
224        static BaseCommandLineArgument& addArgument(const std::string& name, T defaultValue);
225
226    private:
227        //! Constructor initialises bFirstTimeParse_ with true.
228        CommandLine() : bFirstTimeParse_(true) { }
229        //! Undefined copy constructor
230        CommandLine(const CommandLine& instance);
231        ~CommandLine();
232
233        static CommandLine& _getInstance();
234
235        void _parse(const std::vector<std::string>& arguments);
236        void checkFullArgument(const std::string& name, const std::string& value);
237        void checkShortcut(const std::string& shortcut, const std::string& value);
238
239        template <class T>
240        BaseCommandLineArgument* createArg(const std::string& name, T defaultValue, Int2Type<0> isString);
241        template <class T>
242        BaseCommandLineArgument* createArg(const std::string& name, T defaultValue, Int2Type<1> isString);
243
244        /**
245            Tells whether we parse the first expression. The CommmandLineArguments are added before main().
246            So when we call parse() the first time, we need to create a map with all shortcuts since these
247            get added after addCommandLineArgument().
248        */
249        bool bFirstTimeParse_;
250
251        //! Holds all pointers to the arguments and serves as a search map by name.
252        std::map<std::string, BaseCommandLineArgument*> cmdLineArgs_;
253        //! Search map by chortcut for the arguments.
254        std::map<std::string, BaseCommandLineArgument*> cmdLineArgsShortcut_;
255    };
256
257
258    /**
259    @brief
260        Retrieves a CommandLineArgument.
261        The method throws an exception if 'name' was not found or the value could not be converted.
262    @note
263        You shold of course not call this method before the command line has been parsed.
264    */
265    template <class T>
266    const CommandLineArgument<T>* CommandLine::getArgument(const std::string& name)
267    {
268        std::map<std::string, BaseCommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
269        if (it == _getInstance().cmdLineArgs_.end())
270        {
271            ThrowException(Argument, "Could find command line argument '" + name + "'.");
272        }
273        else
274        {
275            CommandLineArgument<T>* arg = dynamic_cast<CommandLineArgument<T>* >(it->second);
276            if (arg == 0)
277            {
278                ThrowException(Argument, "Could not convert command line argument value to requested type. " \
279                    "You should use exactly the same type as predefined.");
280            }
281            else
282            {
283                return arg;
284            }
285        }
286    }
287
288    /**
289    @brief
290        Adds a new CommandLineArgument to the internal map.
291        Note that only such arguments are actually valid.
292    @param name
293        Name of the argument. Shortcut can be added later.
294    @param defaultValue
295        Default value that is used when argument was not given.
296    @note
297        In order to store char* strings as std::string too, there's
298        little bit of template programming involved:
299        StaticConversions::exists determines whether T converts to std::string.
300        Int2Type<int> is then used to call the right function. One returns
301        a CommandLineArgument<T> and the other CommandLineArgument<std::string>.
302    */
303    template <class T>
304    BaseCommandLineArgument& CommandLine::addArgument(const std::string& name, T defaultValue)
305    {
306        std::map<std::string, BaseCommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
307        OrxAssert(it == _getInstance().cmdLineArgs_.end(),
308            "Cannot add a command line argument with name '" + name + "' twice.");
309
310        return *(_getInstance().cmdLineArgs_[name] = 
311            _getInstance().createArg(name, defaultValue, Int2Type<StaticConversion<T, std::string>::exists>()));
312    }
313
314    /**
315        Returns a new CommandLineArgument<T>.
316    */
317    template <class T>
318    BaseCommandLineArgument* CommandLine::createArg(const std::string& name, T defaultValue, Int2Type<0> isPrime)
319    {
320        return new CommandLineArgument<T>(name, defaultValue);
321    }
322
323    /**
324        Returns a new CommandLineArgument<std::string>.
325    */
326    template <class T>
327    BaseCommandLineArgument* CommandLine::createArg(const std::string& name, T defaultValue, Int2Type<1> isPrime)
328    {
329        return new CommandLineArgument<std::string>(name, defaultValue);
330    }
331}
332
333#endif /* _CommandLine_H__ */
Note: See TracBrowser for help on using the repository browser.