Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/command/ConsoleCommandIncludes.h @ 11506

Last change on this file since 11506 was 11071, checked in by landauf, 9 years ago

merged branch cpp11_v3 back to trunk

  • Property svn:eol-style set to native
File size: 14.5 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 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30    @defgroup ConsoleCommand Console commands
31    @ingroup Command
32*/
33
34/**
35    @file
36    @ingroup Command ConsoleCommand
37    @brief Declaration the SetConsoleCommand() macro.
38
39    @anchor ConsoleCommandExample
40
41    Console commands can be used to write scripts, use key-bindings or simply to be
42    entered into the shell by the user. Instances of orxonox::ConsoleCommand define
43    the function of a command, and also more information like, for example, if it is
44    active, default values, and possible arguments.
45
46    Commands need to be registered to the system statically on startup by using the
47    SetConsoleCommand() or DeclareConsoleCommand() macros outside of a function.
48    This ensures that commands are known to the system at any time, so they can be
49    evaluated (see orxonox::CommandExecutor::evaluate()), for example for key-bindings.
50
51    Example:
52    @code
53    void myCoutFunction(const std::string& text)        // Define a static function
54    {
55        orxout() << "Text: " << text << endl;           // Print the text to the console
56    }
57
58    SetConsoleCommand("cout", &myCoutFunction);         // Register the function as command with name "cout"
59    @endcode
60
61    Now you can open the shell and execute the command:
62    @code
63    $ cout Hello World
64    @endcode
65
66    Internally this command is now passed to orxonox::CommandExecutor::execute():
67    @code
68    CommandExecutor::execute("cout HelloWorld");
69    @endcode
70
71    CommandExecutor searches for a command with name "cout" and passes the arguments
72    "Hello World" to it. Because we registered myCoutFunction() with this command,
73    as a result the following text will be printed to the console:
74    @code
75    Text: Hello World
76    @endcode
77
78    You can add more attributes to the ConsoleCommand, by using the command-chain feature
79    of SetConsoleCommand(). For example like this:
80    @code
81    SetConsoleCommand("cout", &myCoutFunction)
82        .addGroup("output", "text")
83        .accessLevel(AccessLevel::Offline)
84        .defaultValues("no text");
85    @endcode
86
87    Open the shell again and try it:
88    @code
89    $ cout Hello World
90    Text: Hello World
91    $ output text Hello World
92    Text: Hello World
93    $ cout
94    Text: no text
95    @endcode
96
97    If you execute it online (note: the access level is "Offline"), you will see the
98    following (or something similar):
99    @code
100    $ cout Hello World
101    Error: Can't execute command "cout", access denied.
102    @endcode
103
104    If a command is executed, the arguments are passed to an underlying function,
105    whitch is wrapped by an orxonox::Functor which again is wrapped by an orxonox::Executor.
106    The Functor contains the function-pointer, as well as the object-pointer in
107    case of a non-static member-function. The executor stores possible default-values
108    for each argument of the function.
109
110    The function of a command can be changed at any time. It's possible to just exchange
111    the function-pointer of the underlying Functor if the headers of the functions are
112    exactly the same. But you can also exchange the Functor itself or even completely
113    replace the Executor. Also the other attributes of a ConsoleCommand can be modified
114    during the game, for example it can be activated or deactivated.
115
116    To do so, the function ModifyConsoleCommand() has to be used. It returns an instance
117    of orxonox::ConsoleCommand::ConsoleCommandManipulator which has an interface similar to
118    orxonox::ConsoleCommand, but with slight differences. You can use it the same way like
119    SetConsoleCommand(), meaning you can use command-chains to change different attributes at
120    the same time. ModifyConsoleCommand() must not be executed statically, but rather in a
121    function at some point of the execution of the program.
122
123    Example:
124    @code
125    void myOtherCoutFunction(const std::string& text)                       // Define a new static function
126    {
127        orxout() << "Uppercase: " << getUppercase(text) << endl;            // Print the text in uppercase to the console
128    }
129
130    {
131        // ...                                                              // somewhere in the code
132
133        ModifyConsoleCommand("cout").setFunction(&myOtherCoutFunction);     // Modify the underlying function of the command
134
135        // ...
136    }
137    @endcode
138
139    If you now enter the command into the shell, you'll see a different behavior:
140    @code
141    $ cout Hello World
142    Uppercase: HELLO WORLD
143    $ cout
144    Uppercase: NO TEXT
145    @endcode
146
147    A few important notes about changing functions:
148
149    Instead of changing the function with setFunction(), you can also create a command-stack
150    by using pushFunction() and popFunction(). It's important to note a few things about that,
151    because the underlying structure of Executor and Functor has a few pitfalls:
152     - If you push a new function-pointer, the same executor as before will be used (and, if
153       the headers match, even the same functor can be used, which is very fast)
154     - If you push a new Functor, the same executor as before will be used
155     - If you push a new Executor, everything is changed
156
157    Note that the executor contains the @b default @b values, so if you just exchange the
158    Functor, the default values remain the same. However if you decide to change the default
159    values at any point of the stack, <b>this will also change the default values on all
160    other stack-levels</b> that share the same executor. If you don't like this behavior,
161    you have to explicitly push a new executor before changing the default values, either by
162    calling pushFunction(executor) or by calling pushFunction(void) which pushes a copy of
163    the current executor to the stack.
164
165    Another important point are object pointers in case of non-static member-functions.
166    Whenever you set or push a new function, <b>you must add the object pointer again</b>
167    because objects are stored in the Functor which is usually exchanged if you change
168    the function.
169
170    You can also use a stack for objects, but note that this <b>object-stack is different for each
171    function</b> - so if you set a new function, the object-stack will be cleared. If you push
172    a new function, the old object-stack is stored in the stack, so it can be restored if
173    you pop the function.
174
175    %DeclareConsoleCommand():
176
177    Appart from SetConsoleCommand() you can also call DeclareConsoleCommand(). In contrast
178    to SetConsoleCommand(), this doesn't assign a function to the command. Indeed you have
179    to pass a function-pointer to DeclareConsoleCommand(), but it is only used to determine
180    the header of the future command-function. This allows to declare a command statically,
181    thus it's possible to evaluate key-bindings of this command, but the actual function
182    can be assigned at a later point.
183
184    Example:
185    @code
186    DeclareConsoleCommand("cout", &prototype::void__string);
187    @endcode
188
189    If you try to execute the command now, you see the following (or something similar):
190    @code
191    $ cout Hello World
192    Error: Can't execute command "cout", command is not active.
193    @endcode
194
195    You first have to assign a function to use the command:
196    @code
197    {
198        // ...
199
200        ModifyConsoleCommand("cout").setFunction(&myCoutFunction);
201
202        // ...
203    }
204    @endcode
205
206    Now you can use it:
207    @code
208    $ cout Hello World
209    Text: Hello World
210    @endcode
211
212    Note that the initial function prototype::void__string is defined in the namespace
213    orxonox::prototype. If there's no function with the desired header, you can extend
214    the collection of functions or simply use another function that has the same header.
215*/
216
217#ifndef _ConsoleCommandIncludes_H__
218#define _ConsoleCommandIncludes_H__
219
220#include "core/CorePrereqs.h"
221
222#include "ConsoleCommand.h"
223#include "ConsoleCommandManager.h"
224#include "util/VA_NARGS.h"
225#include "core/module/StaticallyInitializedInstance.h"
226
227
228/**
229    @brief Defines a console command. The macro is overloaded for 2-4 parameters.
230
231    This is an overloaded macro. Depending on the number of arguments a different
232    overloaded implementation of the macro will be chosen.
233
234    Console commands created with SetConsoleCommand() become active immediately and
235    the given function-pointer (and optionally the object) will be used to execute
236    the command.
237*/
238#define SetConsoleCommand(...) \
239    BOOST_PP_EXPAND(BOOST_PP_CAT(SetConsoleCommand, ORXONOX_VA_NARGS(__VA_ARGS__))(__VA_ARGS__))
240/**
241    @brief This macro is executed if you call SetConsoleCommand() with 2 arguments.
242    @param name The name (string) of the console command
243    @param functionpointer The function-pointer of the corresponding command-function
244*/
245#define SetConsoleCommand2(name, functionpointer) \
246    SetConsoleCommandGeneric("", name, orxonox::createFunctor(functionpointer))
247/**
248    @brief This macro is executed if you call SetConsoleCommand() with 3 arguments.
249    @param group The group (string) of the console command
250    @param name The name (string) of the console command
251    @param functionpointer The function-pointer of the corresponding command-function
252*/
253#define SetConsoleCommand3(group, name, functionpointer) \
254    SetConsoleCommandGeneric(group, name, orxonox::createFunctor(functionpointer))
255/**
256    @brief This macro is executed if you call SetConsoleCommand() with 4 arguments.
257    @param group The group (string) of the console command
258    @param name The name (string) of the console command
259    @param functionpointer The function-pointer of the corresponding command-function
260    @param object The object that will be assigned to the command. Used for member-functions.
261*/
262#define SetConsoleCommand4(group, name, functionpointer, object) \
263    SetConsoleCommandGeneric(group, name, orxonox::createFunctor(functionpointer, object))
264
265/// Internal macro
266#define SetConsoleCommandGeneric(group, name, functor) \
267    static orxonox::ConsoleCommand& BOOST_PP_CAT(__consolecommand_, __UNIQUE_NUMBER__) \
268        = (new orxonox::SI_CC(new orxonox::ConsoleCommand(group, name, orxonox::createExecutor(functor))))->getCommand()
269
270
271/**
272    @brief Declares a console command. The macro is overloaded for 2-3 parameters.
273
274    This is an overloaded macro. Depending on the number of arguments a different
275    overloaded implementation of the macro will be chosen.
276
277    Console commands created with DeclareConsoleCommand() don't use the the given
278    function-pointer to execute the command, it is only used to define the header
279    of the future command-function. The command is inactive until you manually
280    set a function with orxonox::ModifyConsoleCommand(). You can use a different
281    function-pointer than in the final command, as long as it has the same header.
282*/
283#define DeclareConsoleCommand(...) \
284    BOOST_PP_EXPAND(BOOST_PP_CAT(DeclareConsoleCommand, ORXONOX_VA_NARGS(__VA_ARGS__))(__VA_ARGS__))
285/**
286    @brief This macro is executed if you call DeclareConsoleCommand() with 2 arguments.
287    @param name The name (string) of the console command
288    @param functionpointer The function-pointer of an arbitrary function that has the same header as the final function
289*/
290#define DeclareConsoleCommand2(name, functionpointer) \
291    DeclareConsoleCommandGeneric("", name, orxonox::createFunctor(functionpointer))
292/**
293    @brief This macro is executed if you call DeclareConsoleCommand() with 3 arguments.
294    @param group The group (string) of the console command
295    @param name The name (string) of the console command
296    @param functionpointer The function-pointer of an arbitrary function that has the same header as the final function
297*/
298#define DeclareConsoleCommand3(group, name, functionpointer) \
299    DeclareConsoleCommandGeneric(group, name, orxonox::createFunctor(functionpointer))
300
301/// Internal macro
302#define DeclareConsoleCommandGeneric(group, name, functor) \
303    static orxonox::ConsoleCommand& BOOST_PP_CAT(__consolecommand_, __UNIQUE_NUMBER__) \
304        = (new orxonox::SI_CC(new orxonox::ConsoleCommand(group, name, orxonox::createExecutor(functor), false)))->getCommand()
305
306
307namespace orxonox
308{
309    class _CoreExport StaticallyInitializedConsoleCommand : public StaticallyInitializedInstance
310    {
311        public:
312            StaticallyInitializedConsoleCommand(ConsoleCommand* command)
313                : StaticallyInitializedInstance(StaticInitialization::CONSOLE_COMMAND)
314                , command_(command)
315            {}
316            ~StaticallyInitializedConsoleCommand() { delete command_; }
317
318            virtual void load() override;
319            virtual void unload() override;
320
321            inline ConsoleCommand& getCommand()
322                { return *this->command_; }
323
324        private:
325            ConsoleCommand* command_;
326    };
327
328    typedef StaticallyInitializedConsoleCommand SI_CC;
329
330    /**
331        @brief Returns a manipulator for a command with the given name.
332
333        @note If the command doesn't exist, the manipulator contains a nullptr to the command,
334        but it can still be used without checks, because all functions of ConsoleCommandManipulator
335        check internally if the command exists.
336    */
337    inline ConsoleCommand::ConsoleCommandManipulator ModifyConsoleCommand(const std::string& name)
338        { return ConsoleCommandManager::getInstance().getCommand(name, true); }
339    /**
340        @brief Returns a manipulator for a command with the given group and name.
341
342        @note If the command doesn't exist, the manipulator contains a nullptr to the command,
343        but it can still be used without checks, because all functions of ConsoleCommandManipulator
344        check internally if the command exists.
345    */
346    inline ConsoleCommand::ConsoleCommandManipulator ModifyConsoleCommand(const std::string& group, const std::string& name)
347        { return ConsoleCommandManager::getInstance().getCommand(group, name, true); }
348}
349
350#endif /* _ConsoleCommandIncludes_H__ */
Note: See TracBrowser for help on using the repository browser.