source: src/Actions/Action_impl_pre.hpp@ 94232b

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 94232b was a82f61, checked in by Frederik Heber <heber@…>, 9 years ago

Added Action::setOptionValue() which allows setting the option via a string.

  • this is preparatory for allowing the creation of MakroActions inside the code. We need to set the options without going through dialogs in some way or another and without being completely inside the Actions as is the case for the COMMAND variants and the python function calls.
  • Property mode set to 100644
File size: 14.9 KB
Line 
1/*
2 * Action_impl.hpp
3 *
4 * Created on: Aug 25, 2010
5 * Author: heber
6 */
7
8/** These macros define the following functions, necessary but repetitive for
9 * every Action:
10 * -# Dialog* fillDialog()
11 * -# action command (e.g. AnalysisMolecularVolume() )
12 * -# void getParametersfromValuStorage()
13 * -# struct Action...Parameters
14 *
15 * For this, the user has the define the following values, each with
16 * parenthesis, for the values/parameters the action needs
17 * -# paramtypes, e.g. (int)(double)
18 * -# paramtokens, e.g. ("Z")("length")
19 * -# paramreferences, e.g. (Z)(length)
20 * and for additional values/parameters to save in the state
21 * -# statetypes, e.g. (int)(double)
22 * -# statereferences, e.g. (Z)(length)
23 * and the name and category of the action
24 * -# CATEGORY, e.g. Analysis
25 * -# ACTIONNAME, e.g. MolecularVolume
26 */
27
28// include config.h
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32
33#include "CodePatterns/Chronos.hpp"
34
35#include <boost/preprocessor/cat.hpp>
36#include <boost/preprocessor/expand.hpp>
37#include <boost/preprocessor/comparison/equal.hpp>
38#include <boost/preprocessor/comparison/not_equal.hpp>
39#include <boost/preprocessor/control/expr_if.hpp>
40#include <boost/preprocessor/control/if.hpp>
41#include <boost/preprocessor/debug/assert.hpp>
42#include <boost/preprocessor/facilities/empty.hpp>
43#include <boost/preprocessor/iteration/local.hpp>
44#include <boost/preprocessor/list/adt.hpp>
45#include <boost/preprocessor/punctuation/comma_if.hpp>
46#include <boost/preprocessor/repetition/repeat.hpp>
47#include <boost/preprocessor/seq/elem.hpp>
48#include <boost/preprocessor/seq/filter.hpp>
49#include <boost/preprocessor/seq/push_back.hpp>
50#include <boost/preprocessor/seq/seq.hpp>
51#include <boost/preprocessor/seq/size.hpp>
52#include <boost/preprocessor/seq/transform.hpp>
53
54#include "Actions/ActionQueue.hpp"
55#include "Actions/toCLIString.hpp"
56#include "Actions/toPythonString.hpp"
57#include "Parameters/Parameter.hpp"
58
59
60// some derived names: if CATEGORY is not given, we don't prefix with it
61#ifdef CATEGORY
62#define ACTION BOOST_PP_CAT(CATEGORY, BOOST_PP_CAT(ACTIONNAME, Action))
63#define COMMAND BOOST_PP_CAT(CATEGORY, ACTIONNAME)
64#define STATE BOOST_PP_CAT(CATEGORY, BOOST_PP_CAT(ACTIONNAME, State))
65#define PARAMS BOOST_PP_CAT(CATEGORY, BOOST_PP_CAT(ACTIONNAME, Parameters))
66#else
67#define ACTION BOOST_PP_CAT(ACTIONNAME, Action)
68#define COMMAND ACTIONNAME
69#define STATE BOOST_PP_CAT(ACTIONNAME, State)
70#define PARAMS BOOST_PP_CAT(ACTIONNAME, Parameters)
71#endif
72#define INSTANCE BOOST_PP_CAT(this_, BOOST_PP_CAT(ACTIONNAME, _instance))
73
74// check if no lists given
75#ifndef paramtypes
76#define MAXPARAMTYPES 0
77#else
78#define MAXPARAMTYPES BOOST_PP_SEQ_SIZE(paramtypes)
79#endif
80#ifndef statetypes
81#define MAXSTATETYPES 0
82#else
83#define MAXSTATETYPES BOOST_PP_SEQ_SIZE(statetypes)
84#endif
85#ifndef paramdefaults
86#define MAXPARAMDEFAULTS 0
87// this is required for valid_print "else part"
88#define sequencer(z,n,data) \
89 BOOST_PP_SEQ_PUSH_BACK( data, NOPARAM_DEFAULT)
90#define paramdefaults BOOST_PP_REPEAT( MAXPARAMTYPES, sequencer, BOOST_PP_SEQ_NIL )
91#else
92#define MAXPARAMDEFAULTS BOOST_PP_SEQ_SIZE(paramdefaults)
93#endif
94#define PARAM_DEFAULT(x) \
95 (x, BOOST_PP_NIL)
96
97// check user has given name and category
98#ifndef ACTIONNAME
99ERROR: No "ACTIONNAME" defined in: __FILE__
100#endif
101
102// calculate numbers and check whether all have same size
103#ifdef paramtokens
104BOOST_PP_ASSERT_MSG(BOOST_PP_EQUAL(MAXPARAMTYPES, BOOST_PP_SEQ_SIZE(paramtokens)),\
105 ERROR: There are not the same number of "paramtokens" and "paramtypes" in: __FILE__ \
106)
107#endif
108#ifdef paramreferences
109BOOST_PP_ASSERT_MSG(BOOST_PP_EQUAL(MAXPARAMTYPES, BOOST_PP_SEQ_SIZE(paramreferences)),\
110 ERROR: There are not the same number of "paramtokens" and "paramreferences" in: __FILE__ \
111)
112#endif
113#ifdef paramdescriptions
114BOOST_PP_ASSERT_MSG(BOOST_PP_EQUAL(MAXPARAMTYPES, BOOST_PP_SEQ_SIZE(paramdescriptions)),\
115 ERROR: There are not the same number of "paramtokens" and "paramdescriptions" in: __FILE__ \
116)
117#endif
118
119#ifdef statetypes
120BOOST_PP_ASSERT_MSG(BOOST_PP_EQUAL(MAXSTATETYPES, BOOST_PP_SEQ_SIZE(statereferences)),\
121 ERROR: There are not the same number of "statetypes" and "statereferences" in: __FILE__ \
122)
123#endif
124
125// print a list of type ref followed by a separator, i.e. "int i;"
126#define initialiser_print(z,n,initialiserlist) \
127 BOOST_PP_SEQ_ELEM(n, initialiserlist) \
128 (BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(n, initialiserlist))),
129
130// print a list of ref(_ref) followed by a separator, i.e. "id(_id),"
131#define type_print(z,n,TYPELIST, VARLIST, separator) \
132 BOOST_PP_SEQ_ELEM(n, TYPELIST) \
133 BOOST_PP_SEQ_ELEM(n, VARLIST)\
134 separator
135
136// print a list of type ref followed, i.e. "int i, double position"
137#define type_list(z,n,TYPELIST,VARLIST) \
138 BOOST_PP_COMMA_IF(n)\
139 BOOST_PP_SEQ_ELEM(n, TYPELIST) \
140 BOOST_PP_SEQ_ELEM(n, VARLIST)
141
142// prints dialog->query calls for paramtypes with tokens
143#define dialog_print(z,n,unused) \
144 dialog->query<\
145 BOOST_PP_SEQ_ELEM(n, paramtypes)\
146 >( params. \
147 BOOST_PP_SEQ_ELEM(n, paramreferences)\
148 ,\
149 BOOST_PP_SEQ_ELEM(n, paramtokens)\
150 ,\
151 BOOST_PP_SEQ_ELEM(n, paramdescriptions)\
152 );
153
154// prints command line call for this Action for paramtypes with tokens
155#define outputAsCLI_print(z,n,output) \
156 output << \
157 BOOST_PP_IF(n, " --", "--") \
158 << \
159 BOOST_PP_SEQ_ELEM(n, paramtokens) \
160 << " " << toCLIString(params. \
161 BOOST_PP_SEQ_ELEM(n, paramreferences) \
162 .get());
163
164// prints if statement to check two strings (paramtokens[n] vs. TOKEN)
165#define checkpresenttoken_print(z, n, TOKEN, booltoken) \
166 if ( std::string(\
167 BOOST_PP_SEQ_ELEM(n, paramtokens) )\
168 == getName()) \
169 booltoken = false;
170
171// prints command line call for this Action for paramtypes with tokens
172#define outputAsPython_print(z,n,output) \
173 output << \
174 BOOST_PP_IF(n, ", ", "") \
175 << "\"" << toPythonString(params. \
176 BOOST_PP_SEQ_ELEM(n, paramreferences) \
177 .getUnvalidated()) \
178 << "\"";
179
180// print an initialiser list, i.e. "var( token, valid (,default) )(,)"
181#define valid_print(z,n,TOKENLIST, VARLIST, VALIDLIST, DEFAULTLIST) \
182 BOOST_PP_COMMA_IF(n) \
183 BOOST_PP_SEQ_ELEM(n, VARLIST) \
184 ( \
185 BOOST_PP_SEQ_ELEM(n, TOKENLIST) \
186 , \
187 BOOST_PP_SEQ_ELEM(n, VALIDLIST) \
188 BOOST_PP_COMMA_IF( BOOST_PP_NOT( BOOST_PP_LIST_IS_NIL( BOOST_PP_SEQ_ELEM(n, DEFAULTLIST) ) ) ) \
189 BOOST_PP_EXPR_IF( \
190 BOOST_PP_NOT( BOOST_PP_LIST_IS_NIL( BOOST_PP_SEQ_ELEM(n, DEFAULTLIST) ) ), \
191 BOOST_PP_LIST_FIRST( BOOST_PP_SEQ_ELEM(n, DEFAULTLIST) )) \
192 )
193
194// print an initialiser list, i.e. "var( valid . var )(,)"
195#define validcopy_print(z,n,TOKENLIST, VARLIST, VALID) \
196 BOOST_PP_COMMA_IF(n) \
197 BOOST_PP_SEQ_ELEM(n, VARLIST) \
198 ( \
199 VALID . \
200 BOOST_PP_SEQ_ELEM(n, VARLIST) \
201 )
202
203// prints set/queryCurrentValue (command) for paramreferences and paramtokens
204#define value_print(z, n, container, prefix) \
205 prefix \
206 BOOST_PP_SEQ_ELEM(n, container)\
207 .set(\
208 BOOST_PP_SEQ_ELEM(n, container)\
209 );
210
211// prints set/queryCurrentValue (command) for paramreferences and paramtokens
212#define valuetype_print(z,n,container, types, prefix) \
213 prefix \
214 BOOST_PP_SEQ_ELEM(n, container) \
215 .setAsString( \
216 BOOST_PP_SEQ_ELEM(n, container) \
217 );
218
219#define stringtype std::string
220
221#define type2string(s, data, elem) \
222 stringtype
223
224// prints if ( token == "select-molecule-by-id" ) { prefix.ids.setAsString(_value); }
225#define setparameterifmatch_print(z,n,container, reference, token, value) \
226 if ( token == BOOST_PP_SEQ_ELEM(n, container) ) { \
227 params. \
228 BOOST_PP_SEQ_ELEM(n, reference) \
229 .setAsString( \
230 value \
231 ); \
232 }
233
234//#include "Actions/ActionTraits.hpp"
235#include "UIElements/Dialog.hpp"
236
237#ifdef paramtokens
238#define statenecessary 1
239#endif
240#ifndef statetokens
241#define statenecessary 1
242#endif
243
244namespace MoleCuilder {
245
246// =========== memento to remember the state when undoing ===========
247#ifdef statenecessary
248class STATE : public ActionState {
249public:
250 STATE(
251#if defined statetypes && defined statereferences // if we have parameters, we have to add "_" before each reference and add the params as the last one
252#define OP(s,data,elem) BOOST_PP_CAT(data, elem) // OP to add "_"
253#define BOOST_PP_LOCAL_MACRO(n) type_list(~, n, BOOST_PP_SEQ_PUSH_BACK(statetypes, const ACTION::PARAMS &), BOOST_PP_SEQ_TRANSFORM(OP, _, BOOST_PP_SEQ_PUSH_BACK(statereferences, params)))
254#else /// if not, params is only list
255#define BOOST_PP_LOCAL_MACRO(n) type_list(~, n, (const ACTION::PARAMS &), (_params))
256#endif
257#define BOOST_PP_LOCAL_LIMITS (0, MAXSTATETYPES)
258#include BOOST_PP_LOCAL_ITERATE()
259) :
260#if defined statetypes && defined statereferences // do we have parameters at all?
261BOOST_PP_REPEAT(MAXSTATETYPES, initialiser_print, statereferences)
262#endif
263params(_params)
264 {}
265
266#if defined statetypes && defined statereferences // do we have parameters at all?
267#define BOOST_PP_LOCAL_MACRO(n) type_print(~, n, statetypes, statereferences, ;)
268#define BOOST_PP_LOCAL_LIMITS (0, MAXSTATETYPES-1)
269#include BOOST_PP_LOCAL_ITERATE()
270#endif
271 ACTION::PARAMS params;
272};
273#endif /* statenecessary */
274
275// (const) prototype to be placed into the ActionRegistry (must be deleted by registry itself)
276//const ACTION INSTANCE;
277//boost::shared_ptr< ACTION > INSTANCE( new ACTION() );
278
279// =========== constructor ===========
280ACTION::ACTION () :
281#if defined BASECLASS
282 BASECLASS(ActionTraits< ACTION >())
283#else
284 Action(ActionTraits< ACTION >())
285#endif
286{}
287
288// =========== destructor ===========
289ACTION::~ACTION ()
290{
291 //std::cout << "Action ACTION is being destroyed." << std::endl;
292}
293
294// =========== parameter constructor ===========
295ACTION::PARAMS::PARAMS()
296#if defined paramtokens && defined paramreferences && defined paramvalids
297 :
298#define BOOST_PP_LOCAL_MACRO(n) valid_print(~, n, paramtokens, paramreferences, paramvalids, paramdefaults)
299#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
300#include BOOST_PP_LOCAL_ITERATE()
301#endif
302 {}
303
304ACTION::PARAMS::PARAMS(const PARAMS &p)
305#if defined paramtokens && defined paramreferences
306 :
307#define BOOST_PP_LOCAL_MACRO(n) validcopy_print(~, n, paramtokens, paramreferences, p)
308#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
309#include BOOST_PP_LOCAL_ITERATE()
310#endif
311 {}
312
313void ACTION::setOptionValue(const std::string &_token, const std::string &_value)
314{
315 // check whether it is a valid token and set
316#if defined paramtokens && defined paramreferences
317#define BOOST_PP_LOCAL_MACRO(n) setparameterifmatch_print(~, n, paramtokens, paramreferences, _token, _value)
318#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
319#include BOOST_PP_LOCAL_ITERATE()
320#endif
321}
322
323// =========== clone Action ===========
324Action* ACTION::clone(enum QueryOptions flag) const
325{
326 if (flag == Interactive)
327 return new ACTION();
328 else
329 return new ACTION(*this);
330}
331
332// =========== fill a dialog ===========
333Dialog* ACTION::fillDialog(Dialog *dialog) {
334 ASSERT(dialog,"No Dialog given when filling actionname's dialog");
335#if BOOST_PP_EQUAL(MAXPARAMTYPES,0)
336 dialog->queryEmpty(TOKEN, Traits.getDescription());
337#else
338#define BOOST_PP_LOCAL_MACRO(n) dialog_print(~, n, ~)
339#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
340#include BOOST_PP_LOCAL_ITERATE()
341#endif
342 return dialog;
343};
344
345// =========== output as CLI ===========
346void ACTION::outputAsCLI(std::ostream &ost) const {
347 // check whether TOKEN is also an option
348 // is a bit ugly as preprocessor cannot compare strings
349 bool status = true;
350#if defined paramtokens && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
351#define BOOST_PP_LOCAL_MACRO(n) checkpresenttoken_print(~, n, TOKEN, status)
352#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
353#include BOOST_PP_LOCAL_ITERATE()
354#endif
355 if (status) {
356 ost << "--" << TOKEN;
357#if defined paramtypes && defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
358 ost << " ";
359#endif
360 }
361 // then print option along with each argument if set
362#if defined paramtypes && defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
363#define BOOST_PP_LOCAL_MACRO(n) outputAsCLI_print(~, n, ost)
364#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
365#include BOOST_PP_LOCAL_ITERATE()
366#endif
367}
368
369// =========== output as PYTHON ===========
370void ACTION::outputAsPython(std::ostream &ost, const std::string &prefix) const {
371 // print prefix and action command
372 ost << prefix << "." << BOOST_PP_STRINGIZE( COMMAND ) << "(";
373 // then print option along with each argument if set
374#if defined paramtypes && defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
375#define BOOST_PP_LOCAL_MACRO(n) outputAsPython_print(~, n, ost)
376#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
377#include BOOST_PP_LOCAL_ITERATE()
378#endif
379 ost << ")" << std::endl;
380}
381
382// =========== time the action ===========
383// we need this here to have the correct function name
384void ACTION::startTimer() const { Chronos::getInstance().startTiming( std::string( TOKEN ) ); }
385void ACTION::endTimer() const { Chronos::getInstance().endTiming( std::string( TOKEN ) ); }
386
387// =========== command for calling action directly ===========
388void COMMAND(
389#if defined paramtypes && defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
390#define BOOST_PP_LOCAL_MACRO(n) type_list(~, n, paramtypes, paramreferences)
391#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
392#include BOOST_PP_LOCAL_ITERATE()
393#endif
394)
395{
396 ACTION *ToCall = dynamic_cast<ACTION*>(ActionQueue::getInstance().getActionByName( TOKEN )); //->clone(params);
397 //ACTION::PARAMS params;
398#if defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
399#define BOOST_PP_LOCAL_MACRO(n) value_print(~, n, paramreferences, ToCall->params.)
400#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
401#include BOOST_PP_LOCAL_ITERATE()
402#endif
403 Action::insertAction( ToCall, Action::NonInteractive);
404};
405
406void BOOST_PP_CAT( COMMAND, _stringargs)(
407#if defined paramtypes && defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
408#define BOOST_PP_LOCAL_MACRO(n) type_list(~, n, BOOST_PP_SEQ_TRANSFORM( type2string, , paramtypes), paramreferences)
409#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
410#include BOOST_PP_LOCAL_ITERATE()
411#endif
412 ) {
413 ACTION *ToCall = dynamic_cast<ACTION*>(ActionQueue::getInstance().getActionByName( TOKEN )); //->clone(params);
414 //ACTION::PARAMS params;
415#if defined paramtypes && defined paramtypes && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
416#define BOOST_PP_LOCAL_MACRO(n) valuetype_print(~, n, paramreferences, paramtypes, ToCall->params. )
417#define BOOST_PP_LOCAL_LIMITS (0, MAXPARAMTYPES-1)
418#include BOOST_PP_LOCAL_ITERATE()
419#endif
420 Action::insertAction( ToCall, Action::NonInteractive);
421};
422
423}
424
425// free up defines
426#undef paramvalids
427#undef paramtypes
428#undef paramtokens
429#undef paramreferences
430#undef paramdescriptions
431#undef paramdefaults
432#undef MAXPARAMTYPES
433#undef MAXPARAMDEFAULTS
434#undef statetypes
435#undef statereferences
436#undef MAXSTATETYPES
437#undef PARAM_DEFAULT
438
439#undef type2string
440#undef stringtype
441#undef initialiser_print
442#undef type_print
443#undef type_list
444#undef dialog_print
445#undef sequencer
446#undef valid_print
447#undef validcopy_print
448#undef value_print
449#undef valuetype_print
450
451#undef ACTION
452#undef COMMAND
453#undef PARAMS
454#undef STATE
455#undef INSTANCE
456
457#undef ACTIONNAME
458#undef CATEGORY
459#undef TOKEN
Note: See TracBrowser for help on using the repository browser.