FreeCAD / src /Mod /CAM /App /ParamsHelper.h
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* Copyright (c) 2017 Zheng Lei (realthunder) <realthunder.dev@gmail.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
****************************************************************************/
#ifndef PARAMS_HELPER_H
#define PARAMS_HELPER_H
/** \page ParamPage Parameter helper macros
* \ingroup PATH
* Collections of macros for managing groups of parameters.
*
* \section motivation_groups_params Motivation
*
* For an application like FreeCAD, there are often cases where the same set of
* parameters are referred in dozons of different places. The macros here is
* designed to help managing those parameters, so that you can define groups of
* parameters once, and refer them everywhere in groups with simple macro calls for
* all kinds of purposes. Any changing, adding and removing of parameters in the
* group become much easier. And by everywhere, I mean \ref ParamCommon
* "class definition, implementation", \ref ParamProperty "document object properties",
* \ref ParamPy "python c++ classes", and even \ref ParamDoc "doc string",
* pretty much everything except the python code, which although not implemented
* yet, is in fact also possible to be done using C preprocessor (No one says C
* preprocessor must produce C code :). It is also possible (not implemented
* yet) to use macros to generate python wrapper class instead of using
* FreeCAD's current xml python export.
*
* \section Debugging
*
* Extensive use of macros has one noticeable disadvantage, though. If some thing
* goes wrong, the compiler error message is kind of cryptic. If so, first
* double check your macro definition of the parameter is correctly, not missing
* or having extra parenthesis or comma. Then, you can use the CMake
* intermediate file target to get the preprocessor output for checking. For
* example, for a file located at \c src/Mod/CAM/App/Area.cpp,
* \code{.sh}
* cd <your_build_dir>/src/Mod/CAM/App
* make Area.cpp.i
* \endcode
*
* The preprocessed intermediate output will be at,
* \code{.sh}
* <your_build_dir>/src/Mod/CAM/App.CMakeFiles/Path.dir/Area.cpp.i
* \endcode
*
* \section intro_boost_preproc Introduction of Boost.Preprocessor
*
* The macros here make heavy use of the awesome
* [Boost.Preprocessor](http://www.boost.org/libs/preprocessor/) (short for
* Boost.PP). Here are is a brief introduction on Boost.PP concept in order to
* explain why this marco library is designed the way it is.
*
* In Boost.PP, a sequence is defined as,
* \code{.sh}
* (a)(b)(c)...
* \endcode
*
* A sequence cannot be empty. Thus, \c () is not a sequence. And also those
* <tt>a, b, c</tt> here cannot directly contain <tt>,</tt>. These restriction
* is due to the fact that <tt>( ) ,</tt> are among those very few special
* characters recognized by the preprocssor. \c a can itself be a sequence or
* other Boost.PP types, so by right, our parameter can be defined as something
* like
* \code{.sh}
* ((type)(name)(default)...)
* \endcode
*
* A bit awkward to write. So another Boost.PP type is chosen, tuple, to define
* each individual parameter. A tuple is defined as
* \code{.sh}
* (a,b,c ...)
* \endcode
*
* This is why the parameter definition requires a double parenthesis, as shown
* in the following section.
*
* \section Library Overview
*
* In this macro library, a parameter is defined using a tuple inside a sequence,
* \code{.sh}
* ((<type>, <arg>, <name>, <default>, <doc>, <seq>, <info>))
* \endcode
*
* - \c type is the type of the parameter. Currently only five types of
* parameters are defined, <tt>short, long, double, bool, enum, enum2</tt>.
* \enum2 type is the same as \enum with additional information to be able to
* map to a user defined C enum type. To add more types, search this file for
* keyword \a _short, and supply all relevant macros. It's quite trivial
* actually.
*
* - \c arg is the argument name. It is intended to be used as function argument.
* By convention, the name shall be all small cases, but that's not required.
* This \c arg can be repurposed, if the parameter is not going to be used as
* function argument. The #AREA_PARAMS_CAREA parameters repurposed this field
* to CArea internal setting variables to implement save, apply and restore
* function using CAreaConfig class.
*
* - \c name is normally a %CamelCase name which are used as member variable and
* property name. Because of this, make sure the names are unique to avoid
* conflicts.
*
* - \c default is the default value of this parameter. Right now, you must
* supply a default value. Boost.PP has trouble dealing with empty values.
* Remember that a sequence cannot be empty. Neither can tuple. Only array,
* something like <tt>(0,())</tt> for an empty array. It is awkward to write,
* and didn't add much functionality I want, hence the restriction of
* non-empty defaults here.
*
* - \c doc is a string to describe the variable.
*
* - \c seq \anchor ParamSeq. Right now this field is used by \c enum and
* \c enum2 type parameter to define its enumerations. As the name suggests,
* it must be a sequence. It is not a tuple because looping through tuple is
* not as easy as sequence. Other type of parameter do not need to have this
* field
*
* - \c info is used to provide the supplimentery information for \c enum2 type
* of parameter, which can be converted to a user defined enum type by
* #PARAM_ENUM_CONVERT. \c info must be a tuple, with the user defined enum
* type as the first element, and a prefix as the second element. For \c enum2
* type of parameter, this field is mandatory.
*
* The common usage is that you define a macro of a group of parameters. And use
* the macro helper here to do operation on each parameter in the group. See
* AreaParams.h file for an example of parameter definitions.
*
* Area.h, Area.cpp, FeatureArea.h, FeatureArea.cpp for usage of variouse macros.
*
* See struct AreaDoc for an example of doc string generation.
*
* Each field of the parameter can be referred to with various
* \ref ParamAccessor "various accessor macros", and can be easily
* \ref ParamStringizer "stringified".
*
* \anchor ParamField You can also use #PARAM_FIELD(_field,_param) to refer to
* each field, where \a _field is one of <tt>TYPE, ARG, NAME, DEF, DOC, or SEQ</tt>.
* And #PARAM_FIELD_STR to stringify.
*
* Here \a _param is the parameter definition described above in the form of a
* Boost.PP tuple, and is usually supplied by various \ref ParamLooper "looper macros"
*
* You can of course directly use various Boost.PP sequence looper to pass
* additional arguments to the operation macro. See #PARAM_PY_DICT_SET_VALUE for
* an example of using tuple, and the more complex example #PARAM_ENUM_CONVERT
*
* Note that when generating comma separated list, the first and last comma are
* conveniently omitted, so that the macros can be mixed with others intuitively
*/
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/pop_front.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/logical/not.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/comparison/greater.hpp>
/** \defgroup ParamHelper Parameters helper macros
* \ingroup PATH
* Collections of macros for managing groups of parameters */
/**
* \defgroup ParamAccessor Field accessors
* To abstract parameter field details
* \ingroup ParamHelper
* @{
*/
#define PARAM_ITYPE 0
#define PARAM_IARG 1
#define PARAM_INAME 2
#define PARAM_IDEF 3
#define PARAM_IDOC 4
#define PARAM_ISEQ 5
#define PARAM_IPROP 5
#define PARAM_IINFO 6
#define PARAM_FIELD(_idx, _param) BOOST_PP_TUPLE_ELEM(PARAM_I##_idx, _param)
#define PARAM_FTYPE(_param) PARAM_FIELD(TYPE, _param)
#define PARAM_FARG(_param) PARAM_FIELD(ARG, _param)
#define PARAM_FNAME(_param) PARAM_FIELD(NAME, _param)
#define PARAM_FDEF(_param) PARAM_FIELD(DEF, _param)
#define PARAM_FDOC(_param) PARAM_FIELD(DOC, _param)
#define PARAM_FSEQ(_param) PARAM_FIELD(SEQ, _param)
#define PARAM_FPROP(_param) PARAM_FIELD(PROP, _param)
#define PARAM_FINFO(_param) PARAM_FIELD(INFO, _param)
#define PARAM_FENUM_TYPE(_param) BOOST_PP_TUPLE_ELEM(0, PARAM_FINFO(_param))
#define PARAM_FENUM_PREFIX(_param) BOOST_PP_TUPLE_ELEM(1, PARAM_FINFO(_param))
/** @} */
/**
* \defgroup ParamStringizer Field stringizers
* \ingroup ParamHelper
* @{ */
#define PARAM_FIELD_STR(_idx, _param) BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(PARAM_I##_idx,_param))
#define PARAM_FTYPE_STR(_param) PARAM_FIELD_STR(TYPE, _param)
#define PARAM_FARG_STR(_param) PARAM_FIELD_STR(ARG, _param)
#define PARAM_FNAME_STR(_param) PARAM_FIELD_STR(NAME, _param)
#define PARAM_FDEF_STR(_param) PARAM_FIELD_STR(DEF, _param)
/** @} */
/** Helper for #PARAM_FSEQ_STR */
#define PARAM_FSEQ_STR_(_i, _elem) BOOST_PP_COMMA_IF(_i) BOOST_PP_STRINGIZE(_elem)
/** \c SEQ stringizer will stringify each element separately
*
* Expands to:
* #seq[0], #seq[1] ...
* \ingroup ParamHelper
*/
#define PARAM_FSEQ_STR(_param) PARAM_FOREACH_I(PARAM_FSEQ_STR_, PARAM_FSEQ(_param))
/** \defgroup ParamLooper Looper macros
* Macros for looping through sequence to parameters
* \ingroup ParamHelper
*/
/** Helper for #PARAM_FOREACH */
#define PARAM_FOREACH_(_, _op, _param) _op(_param)
/** Apply macro \a _op to each parameter in sequence \a _seq
*
* Operation macro \a _op should be defined as,
* \code
* _op(_param)
* \endcode
* \ingroup ParamLooper
*/
#define PARAM_FOREACH(_op, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_FOREACH_, _op, _seq)
/** Helper for #PARAM_FOREACH_I */
#define PARAM_FOREACH_I_(_, _op, _i, _param) _op(_i, _param)
/** Apply macro \a _op to each parameter in sequence \a _seq with additional index
*
* Operation macro \a _op should be defined as,
* \code
* _op(_i,_param)
* \endcode
* \ingroup ParamLooper
* */
#define PARAM_FOREACH_I(_op, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_FOREACH_I_, _op, _seq)
/** Helper for #PARAM_TYPED_FOREACH */
#define PARAM_TYPED_FOREACH_(_1, _op, _param) PARAM_TYPED(_op, _param)(_param)
/** Type depended macro construction
*
* Convert macro \a _op to \a _op##\<type\>. Note that it only converts the
* macro name, not contsucts a macro call. To expand to a macro call, simply
* \code
* PARAM_TYPED(_op,_param)(_param)
* \endcode
* \ingroup ParamLooper
*/
#define PARAM_TYPED(_op, _param) BOOST_PP_CAT(_op, PARAM_FTYPE(_param))
/** Apply type dependent macro call to a sequence of parameters
*
* \a _op will be converted to \a _op##\<type\> for each parameter
* \ingroup ParamLooper
*/
#define PARAM_TYPED_FOREACH(_op, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_TYPED_FOREACH_, _op, _seq)
/** \defgroup ParamCommon Common helpers
* \ingroup ParamHelper
*/
#define PARAM_TYPE_short short
#define PARAM_TYPE_long long
#define PARAM_TYPE_double double
#define PARAM_TYPE_bool bool
#define PARAM_TYPE_enum short
#define PARAM_TYPE_enum2 short
/** Obtain parameter type
*
* The main purpose is to alias enum type to short
* \ingroup ParamCommon
*/
#define PARAM_TYPE(_param) PARAM_TYPED(PARAM_TYPE_, _param)
/** Helper for #PARAM_DECLARE */
#define PARAM_DECLARE_(_1, _src, _param) PARAM_TYPE(_param) _src(_param);
/**
* Declares parameters using the given field as name
*
* \arg \c _src: \anchor ParamSrc Macro to generate source variable. The
* signature must be <tt>_src(_param)<\tt>, where \c _param is the tuple
* defining the parameter. You pass any of the \ref ParamAccessor "parameter
* accessors" to directly access the field. Or, supply your own macro to append
* any prefix as you like. For example:
* \code{.unparsed}
* #define MY_SRC(_param) BOOST_PP_CAT(my,PARAM_FNAME(_param))
* ->
* my##<name>
* \endcode
*
* Expands to:
* \code{.unparsed}
* type1 _src(_param1);type2 _src(_param2); ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_DECLARE(_src, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_, _src, _seq)
/** Helper for #PARAM_DECLARE_INIT */
#define PARAM_DECLARE_INIT_(_1, _src, _param) PARAM_TYPE(_param) _src(_param) = PARAM_FDEF(_param);
/**
* Declares parameters with initialization to default using the given field as
* name
*
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
* more details
*
* Expands to:
* \code{.unparsed}
* type1 _src(_param1)=_def1;type2 _src(_param2)=_def2; ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_DECLARE_INIT(_src, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_INIT_, _src, _seq)
#define PARAM_ENUM_DECLARE_enum_(_1, _name, _i, _elem) \
BOOST_PP_COMMA_IF(_i) BOOST_PP_CAT(_name, _elem)
#define PARAM_ENUM_DECLARE_enum(_param) \
enum \
{ \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_ENUM_DECLARE_enum_, PARAM_FNAME(_param), PARAM_FSEQ(_param)) \
};
#define PARAM_ENUM_DECLARE_short(_param)
#define PARAM_ENUM_DECLARE_long(_param)
#define PARAM_ENUM_DECLARE_double(_param)
#define PARAM_ENUM_DECLARE_bool(_param)
#define PARAM_ENUM_DECLARE_enum2 PARAM_ENUM_DECLARE_enum
/** \defgroup ParamEnumHelper Enum convert helpers
* \ingroup ParamCommon
*/
/** Make anonymous \c enum type
*
* Make anonymous \c enum type for \c enum type parameters in \a _seq. All other
* types are ignored. The enum member is prefixed with \a _name. Expand to:
* \code{.unparsed}
* enum {_name1##_seq1[0], _name1##_seq1[1] ...};
* enum {_name2##_seq2[0], _name2##_seq2[1] ...};
* ...
* \endcode
* \ingroup ParamEnumHelper*/
#define PARAM_ENUM_DECLARE(_seq) PARAM_TYPED_FOREACH(PARAM_ENUM_DECLARE_, _seq)
/** \addgroup ParamEnumHelper Enum convert helpers
* @{ */
#define PARAM_ENUM_CONVERT_short(...)
#define PARAM_ENUM_CONVERT_long(...)
#define PARAM_ENUM_CONVERT_double(...)
#define PARAM_ENUM_CONVERT_bool(...)
#define PARAM_ENUM_CONVERT_enum(...)
#define PARAM_ENUM_CONVERT_enum2 PARAM_ENUM_CONVERT_SINGLE
#define PARAM_ENUM_CONVERT_enum_(_dst, _name, _prefix, _elem) \
case BOOST_PP_CAT(_name, _elem): \
_dst = BOOST_PP_CAT(_prefix, _elem); \
break;
#define PARAM_ENUM_CONVERT__(_1, _args, _i, _elem) \
PARAM_ENUM_CONVERT_enum_( \
BOOST_PP_TUPLE_ELEM(0, _args), \
BOOST_PP_TUPLE_ELEM(1, _args), \
BOOST_PP_TUPLE_ELEM(2, _args), \
_elem \
);
#define PARAM_ENUM_CONVERT_(_1, _args, _param) \
PARAM_TYPED(PARAM_ENUM_CONVERT_, _param) \
(BOOST_PP_TUPLE_ELEM(0, _args), BOOST_PP_TUPLE_ELEM(1, _args), BOOST_PP_TUPLE_ELEM(2, _args), _param)
/** Convert single enum parameter value into user defined enum type
*
* This macro is used by #PARAM_ENUM_CONVERT to convert each parameter, but
* you can use it directly for a single parameter. Check #PARAM_NUM_CONVERT
* for more detail. Make sure the outer parenthesis of \c _param is stripped,
* i.e. not double but single parenthesis
*/
#define PARAM_ENUM_CONVERT_SINGLE(_src, _dst, _default, _param) \
PARAM_FENUM_TYPE(_param) _dst(_param); \
switch (_src(_param)) { \
BOOST_PP_SEQ_FOR_EACH_I( \
PARAM_ENUM_CONVERT__, \
(_dst(_param), PARAM_FNAME(_param), PARAM_FENUM_PREFIX(_param)), \
PARAM_FSEQ(_param) \
) \
default: \
_default(_param); \
}
/** Default handling in #PARAM_ENUM_CONVERT and #PARAM_ENUM_CHECK*/
#define PARAM_ENUM_EXCEPT(_param) \
throw Base::ValueError("invalid value for enum " PARAM_FNAME_STR(_param))
/** @} */
/* Convert ParamHelper defined enum type to user defined ones
*
* This assumes the user defined enum type is given in \ref ParamSeq "seq_type"
* of the parameter definition, and it has the same postfix as the ones
* specified in \ref ParamSeq "seq" member of the parameter definition. See
* \ref ParamEnumHelper "here" for implementations
*
* \ingroup ParamEnumHelper
*
* \arg \c _src: Macro to generate source variable. The signature must be
* <tt>_src(_param)<\tt>, where \c _param is the tuple defining the parameter.
* You pass any of the \ref ParamAccessor "parameter accessors" to directly
* access the field. Or, supply your own macro to append any prefix as you
* like.
* \arg \c _dst: Same as above.
* \arg \c _default: A macro to call for invalid value. Signature should be
* <tt>_default(_param)<\tt>, where \c _param is the parameter definition. You
* can use #PARAM_ENUM_EXCEPT to throw Base::ValueError exception in FreeCAD
* \arg \c _seq: Parameter sequence
*
* For example, with the following parameter definition
* \code{.unparsed}
* #define MY_PARAM_TEST \
* ((enum,test1,Test1,0,"it's a test",(Foo)(Bar),(MyEnum1,myEnum1)) \
* ((enum,test2,Test2,0,"it's a test",(Foo)(Bar),(MyEnum2,myEnum2)))
*
* #define MY_DST(_param) BOOST_PP_CAT(my,PARAM_FNAME(_param))
* \code{.unparsed}
*
* calling
* \code{.unparsed}
* PARAM_ENUM_CONVERT(PARAM_FNAME,MY_DST,My,PARAM_ENUM_EXCEP,MY_PARAM_TEST)
* \code{.unparsed}
*
* expands to
* \code{.unparsed}
* MyEnum1 myTest1;
* switch(Test1) {
* case Test1Foo:
* myTest1 = myEnum1Foo;
* break;
* case Test1Bar:
* myTest1 = myEnum1Bar;
* break;
* default:
* throw Base::ValueError("invalid value for enum Test1");
* }
* MyEnum2 myTest2;
* switch(Test2) {
* case Test1Foo:
* myTest2 = myEnum2Foo;
* break;
* case Test2Bar:
* myTest2 = myEnum2Bar;
* break;
* default:
* throw Base::ValueError("invalid value for enum Test2");
* }
* \endcode
*
* The above code assumes you've already defined \a Test1 and \a Test2 some
* where as the source variable.
*/
#define PARAM_ENUM_CONVERT(_src, _dst, _default, _seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_ENUM_CONVERT_, (_src, _dst, _default), _seq)
#define PARAM_ENUM_CHECK_short(...)
#define PARAM_ENUM_CHECK_long(...)
#define PARAM_ENUM_CHECK_double(...)
#define PARAM_ENUM_CHECK_bool(...)
#define PARAM_ENUM_CHECK_enum PARAM_ENUM_CHECK_SINGLE
#define PARAM_ENUM_CHECK_enum2 PARAM_ENUM_CHECK_SINGLE
#define PARAM_ENUM_CHECK_enum_(_1, _name, _i, _elem) \
case BOOST_PP_CAT(_name, _elem): \
break;
#define PARAM_ENUM_CHECK_(_1, _args, _param) \
PARAM_TYPED(PARAM_ENUM_CHECK_, _param) \
(BOOST_PP_TUPLE_ELEM(0, _args), BOOST_PP_TUPLE_ELEM(1, _args), _param)
#define PARAM_ENUM_CHECK_SINGLE(_src, _default, _param) \
switch (_src(_param)) { \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_ENUM_CHECK_enum_, PARAM_FNAME(_param), PARAM_FSEQ(_param)) \
default: \
_default(_param); \
}
/* Validate enum type parameters
*
* This macro validates the value a variable of enum type parameters. See
* similar macro #PARAM_ENUM_CONVERT for detail usage.
*
* \ingroup ParamEnumHelper
*
* \arg \c _src: Macro to generate source variable. The signature must be
* <tt>_src(_param)<\tt>, where \c _param is the tuple defining the parameter.
* You pass any of the \ref ParamAccessor "parameter accessors" to directly
* access the field. Or, supply your own macro to append any prefix as you
* like.
*
* \arg \c _default: A macro to call for invalid value. Signature should be
* <tt>_default(_param)<\tt>, where \c _param is the parameter definition. You
* can use #PARAM_ENUM_EXCEPT to throw Base::ValueError exception in FreeCAD
*
* \arg \c _seq: Parameter sequence
*/
#define PARAM_ENUM_CHECK(_src, _default, _seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_ENUM_CHECK_, (_src, _default), _seq)
#define PARAM_ENUM_STRING_DECLARE_short(...)
#define PARAM_ENUM_STRING_DECLARE_long(...)
#define PARAM_ENUM_STRING_DECLARE_double(...)
#define PARAM_ENUM_STRING_DECLARE_bool(...)
#define PARAM_ENUM_STRING_DECLARE_enum2 PARAM_ENUM_STRING_DECLARE_enum
/** Helper for #PARAM_ENUM_STRING_DECLARE */
#define PARAM_ENUM_STRING_DECLARE_enum(_prefix, _param) \
BOOST_PP_CAT(_prefix, PARAM_FNAME(_param))[] = {PARAM_FSEQ_STR(_param), NULL};
/** Helper for #PARAM_ENUM_STRING_DECLARE */
#define PARAM_ENUM_STRING_DECLARE_(_1, _prefix, _param) \
PARAM_TYPED(PARAM_ENUM_STRING_DECLARE_, _param)(_prefix, _param)
/** Make \c enum string list
*
* Roughly translated:
* \code{.unparsed}
* _prefix##_name1[] = {#seq1[0], #seq1[1], ...,NULL};
* _prefix##_name2[] = {#seq2[0], #seq2[1], ...,NULL};
* ...
* \endcode
* Example usage:
* PARAM_ENUM_STRING_DECLARE(static const char *Enum, MyParamsSeq)
* \ingroup ParamEnumHelper
*/
#define PARAM_ENUM_STRING_DECLARE(_prefix, _seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_ENUM_STRING_DECLARE_, _prefix, _seq)
/** Helper for #PARAM_INIT */
#define PARAM_INIT_(_, _src, _i, _param) BOOST_PP_COMMA_IF(_i) _src(_param)(PARAM_FDEF(_param))
/** Constructor initialization
*
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
* more details
*
* Expand to,
* \code{.unparsed}
* _src(_param1)(def1), _src(_param1)(def2)...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_INIT(_src, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_INIT_, _src, _seq)
/** Helper for #PARAM_OP */
#define PARAM_OP_(_, _args, _param) \
BOOST_PP_TUPLE_ELEM(0, _args) \
(_param) BOOST_PP_TUPLE_ELEM(1, _args) BOOST_PP_TUPLE_ELEM(2, _args)(_param);
/** Perform operation on two instance of each parameter in a sequence
*
* \arg \c _src: Macro to generate source variable. The signature must be
* <tt>_src(_param)<\tt>, where \c _param is the tuple defining the parameter.
* You pass any of the \ref ParamAccessor "parameter accessors" to directly
* access the field. Or, supply your own macro to append any prefix as you
* like.
* \arg \c _op: a boolean operator
* \arg \c _dst: Same as \c _src above.
*
* Expands to:
* \code{.unparsed}
* _src(_param1) _op _src(_param2);
* \endcode
*
* \ingroup ParamCommon
*/
#define PARAM_OP(_src, _op, _dst, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_COPY_, (_src, _op, _dst), _seq)
/** Helper for #PARAM_ARGS_DEF */
#define PARAM_ARGS_DEF_(_, _src, _i, _param) \
BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) _src(_param) = PARAM_FDEF(_param)
/** Declare the parameters as function argument list with defaults.
*
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
* more details
*
* Expand to:
* \code{.unparsed}
* type1 _src(_param1)=def1, type2 _src(_param1)=def2 ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_ARGS_DEF(_src, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_DEF_, _src, _seq)
/** Helper for #PARAM_ARGS */
#define PARAM_ARGS_(_, _src, _i, _param) BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) _src(_param)
/** Declare the parameters as function argument list without defaults.
*
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
* more details
*
* Expand to:
* \code{.unparsed}
* type1 _src(_param1), type2 _src(_param2) ...
* \endcode
* \ingroup ParamCommon
*/
#define PARAM_ARGS(_src, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_, _src, _seq)
/** \defgroup ParamPy Python helper
* Helper macros for Python bindings
* \ingroup ParamHelper
*/
/** \defgroup ParamDoc Python doc helper
* Generate argument doc string for Python
* \ingroup ParamPy
*/
/** Helper for #PARAM_PY_DOC_enum */
#define PARAM_PY_DOC_enum_(_i, _elem) BOOST_PP_IF(_i, ",", " ") #_i "=" #_elem
/** Generate doc for an enum parameter */
#define PARAM_PY_DOC_enum(_field, _param) \
"\n* " PARAM_FIELD_STR(_field, _param) "(" PARAM_FDEF_STR( \
_param \
) "):" PARAM_FOREACH_I(PARAM_PY_DOC_enum_, PARAM_FSEQ(_param)) ". " PARAM_FDOC(_param) "\n"
/* Generate doc for other type of parameter */
#define PARAM_PY_DOC_short(_field, _param) \
"\n* " PARAM_FIELD_STR(_field, _param) "(" PARAM_FDEF_STR(_param) "): " PARAM_FDOC(_param) "\n"
#define PARAM_PY_DOC_long PARAM_PY_DOC_short
#define PARAM_PY_DOC_double PARAM_PY_DOC_short
#define PARAM_PY_DOC_bool PARAM_PY_DOC_short
#define PARAM_PY_DOC_enum2 PARAM_PY_DOC_enum
#define PARAM_PY_DOC_(_, _field, _param) PARAM_TYPED(PARAM_PY_DOC_, _param)(_field, _param)
/* Generate document of a sequence of parameters
* \ingroup ParamDoc
*/
#define PARAM_PY_DOC(_field, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DOC_, _field, _seq)
/** Helper for #PARAM_PY_ARGS_DOC */
#define PARAM_PY_ARGS_DOC_(_, _field, _i, _param) \
BOOST_PP_IF(_i, ", ", " ") PARAM_FIELD_STR(_field, _param) "=" PARAM_FDEF_STR(_param)
/** Generate argument list string
* \arg \c _field: specifies the \ref ParamField "field" to use as name
*
* Expand to a single string:
* \code{.unparsed}
* "_field1=_def1,_field2=_def2 ..."
* \endcode
*
* \ingroup ParamDoc
*/
#define PARAM_PY_ARGS_DOC(_field, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_PY_ARGS_DOC_, _field, _seq)
/** Helper for #PARAM_FIELDS */
#define PARAM_FIELDS_(_1, _src, _i, _param) BOOST_PP_COMMA_IF(_i) _src(_param)
/** Expand to a list of the given field in the parameter sequence
*
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
* more details
*
* For example, PARAM_FIELDS(PARAM_FARG, _seq) expands to:
* \code{.unparsed}
* arg1,arg2 ...
* \endcode
* \ingroup ParamCommon ParamPy
*/
#define PARAM_FIELDS(_src, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_FIELDS_, _src, _seq)
#define PARAM_PY_CAST_short(_v) (_v)
#define PARAM_PY_CAST_long(_v) (_v)
#define PARAM_PY_CAST_double(_v) (_v)
#define PARAM_PY_CAST_bool(_v) ((_v) ? Py_True : Py_False)
#define PARAM_PY_CAST_enum(_v) (_v)
#define PARAM_PY_CAST_enum2(_v) (_v)
#define PARAM_CAST_PY_short(_v) (_v)
#define PARAM_CAST_PY_long(_v) (_v)
#define PARAM_CAST_PY_double(_v) (_v)
#define PARAM_CAST_PY_bool(_v) (PyObject_IsTrue(_v) ? true : false)
#define PARAM_CAST_PY_enum(_v) (_v)
#define PARAM_CAST_PY_enum2(_v) (_v)
/** Helper for #PARAM_PY_FIELDS */
#define PARAM_PY_FIELDS_(_1, _src, _i, _param) \
BOOST_PP_COMMA_IF(_i) PARAM_TYPED(PARAM_CAST_PY_, _param)(_src(_param))
/** Expand to a comma separated list of the given field in the sequence
*
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
* more details
*
* The field will be casted from python C to C type
* \ingroup ParamCommon ParamPy
*/
#define PARAM_PY_FIELDS(_src, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_PY_FIELDS_, _src, _seq)
/** Helper for #PARAM_FIELD_STRINGS */
#define PARAM_FIELD_STRINGS_(_1, _field, _i, _param) \
BOOST_PP_COMMA_IF(_i) PARAM_FIELD_STR(_field, _param)
/** Expand to a list of stringified fields
* \ingroup ParamStringizer ParamPy
*/
#define PARAM_FIELD_STRINGS(_field, _seq) \
BOOST_PP_SEQ_FOR_EACH_I(PARAM_FIELD_STRINGS_, _field, _seq)
#define PARAM_PYARG_short "h"
#define PARAM_PYARG_long "l"
#define PARAM_PYARG_double "d"
#define PARAM_PYARG_bool "O"
#define PARAM_PYARG_enum "h"
#define PARAM_PYARG_enum2 "h"
/** Helper for #PARAM_PY_KWDS */
#define PARAM_PY_KWDS_(_param) PARAM_TYPED(PARAM_PYARG_, _param)
/** Generate a format string for keywords based argument
* \ingroup ParamPy
*/
#define PARAM_PY_KWDS(_seq) PARAM_FOREACH(PARAM_PY_KWDS_, _seq)
#define PARAM_PY_TYPE_short short
#define PARAM_PY_TYPE_long long
#define PARAM_PY_TYPE_double double
#define PARAM_PY_TYPE_bool PyObject*
#define PARAM_PY_TYPE_enum short
#define PARAM_PY_TYPE_enum2 short
/** Helper for #PARAM_PY_DECLARE */
#define PARAM_PY_DECLARE_(_1, _src, _param) PARAM_TYPED(PARAM_PY_TYPE_, _param) _src(_param);
/** Declare field variables for Python C type without initialization
* \ingroup ParamPy
*/
#define PARAM_PY_DECLARE(_src, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_, _src, _seq)
#define PARAM_PY_INIT_short(_v) _v
#define PARAM_PY_INIT_long(_v) _v
#define PARAM_PY_INIT_double(_v) _v
#define PARAM_PY_INIT_bool(_v) ((_v) ? Py_True : Py_False)
#define PARAM_PY_INIT_enum(_v) _v
#define PARAM_PY_INIT_enum2(_v) _v
/** Helper for #PARAM_PY_DECLARE_INIT */
#define PARAM_PY_DECLARE_INIT_(_1, _src, _param) \
PARAM_TYPED(PARAM_PY_TYPE_, _param) \
_src(_param) = PARAM_TYPED(PARAM_PY_INIT_, _param)(PARAM_FDEF(_param));
/** Declare field variables of Python c type with initialization to default
* \ingroup ParamPy
*/
#define PARAM_PY_DECLARE_INIT(_src, _seq) BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_INIT_, _src, _seq)
/** Helper for #PARAM_REF */
#define PARAM_REF_(_1, _src, _i, _param) BOOST_PP_COMMA_IF(_i) & _src(_param)
/** Generate a list of field references
*
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
*
* more details
* Expand to:
* \code{.unparsed}
* &_src(_param1), &_src(_param1) ...
* \endcode
* \ingroup ParamPy
*/
#define PARAM_REF(_src, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_REF_, _src, _seq)
#define PARAM_CAST_PYOBJ_short(_v) PyLong_FromLong(_v)
#define PARAM_CAST_PYOBJ_long(_v) PyLong_FromLong(_v)
#define PARAM_CAST_PYOBJ_double(_v) PyFloat_FromDouble(_v)
#define PARAM_CAST_PYOBJ_bool(_v) ((_v) ? Py_True : Py_False)
#define PARAM_CAST_PYOBJ_enum PARAM_CAST_PYOBJ_short
#define PARAM_CAST_PYOBJ_enum2 PARAM_CAST_PYOBJ_short
/** Stringize field to a Python string
* \ingroup ParamPy ParamStringizer
*/
#define PARAM_PY_STR(_field, _param) PyUnicode_FromString(PARAM_FIELD_STR(_field, _param))
/** Helper for #PARAM_PY_DICT_SET_VALUE */
#define PARAM_PY_DICT_SET_VALUE_(_1, _args, _param) \
PyDict_SetItem( \
BOOST_PP_TUPLE_ELEM(0, _args), \
PARAM_PY_STR(BOOST_PP_TUPLE_ELEM(1, _args), _param), \
PARAM_TYPED(PARAM_CAST_PYOBJ_, _param)(BOOST_PP_TUPLE_ELEM(2, _args)(_param)) \
);
/** Populate a Python dict with a structure variable
*
* \arg \c _dict: the Python dictionary object
* \arg \c _field: specifies the \ref ParamField "field" to use as key
* \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for
* more details
*
* Roughly translated to:
* \code{.unparsed}
* PyDict_SetItem(_dict,#_field1,_src(_param));
* PyDict_SetItem(_dict,#_field2,_src(_param));
* ...
* \endcode
* \ingroup ParamPy
*/
#define PARAM_PY_DICT_SET_VALUE(_dict, _field, _src, _seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_VALUE_, (_dict, _field, _src), _seq)
#define PARAM_PY_DICT_DOC_enum_(_i, _elem) BOOST_PP_IF(_i, ",", " ") #_i "=" #_elem
/** Generate doc for an enum parameter */
#define PARAM_PY_DICT_DOC_enum(_param) \
"(" PARAM_FDEF_STR( \
_param \
) ") - " PARAM_FOREACH_I(PARAM_PY_DOC_enum_, PARAM_FSEQ(_param)) ".\n" PARAM_FDOC(_param) "\n"
/* Generate doc for other type of parameter */
#define PARAM_PY_DICT_DOC_(_param) "(" PARAM_FDEF_STR(_param) ") - " PARAM_FDOC(_param) "\n"
#define PARAM_PY_DICT_DOC_short PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_long PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_double PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_bool PARAM_PY_DICT_DOC_
#define PARAM_PY_DICT_DOC_enum2 PARAM_PY_DICT_DOC_enum
/** Helper for #PARAM_PY_DICT_SET_DOC */
#define PARAM_PY_DICT_SET_DOC_(_1, _args, _param) \
PyDict_SetItem( \
BOOST_PP_TUPLE_ELEM(0, _args), \
PARAM_PY_STR(BOOST_PP_TUPLE_ELEM(1, _args), _param), \
PyUnicode_FromString(PARAM_TYPED(PARAM_PY_DICT_DOC_, _param)(_param)) \
);
/** Populate a Python dict with the doc field of the parameter sequence
*
* \arg \c _dict: the Python dictionary object
* \arg \c _field: specifies the \ref ParamField "field" to use as key
*
* Roughly translated to:
* \code{.unparsed}
* PyDict_SetItem(_dict,#_field1,doc1);
* PyDict_SetItem(_dict,#_field1,doc2);
* ...
* \endcode
* \ingroup ParamDoc
*/
#define PARAM_PY_DICT_SET_DOC(_dict, _field, _seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_DOC_, (_dict, _field), _seq)
/** \defgroup ParamProperty Property Macros
* Helper macros for FreeCAD properties
* \ingroup ParamHelper
* @{*/
#define PARAM_PROP_bool(_param) App::PropertyBool PARAM_FNAME(_param)
#define PARAM_PROP_double(_param) PARAM_FPROP(_param) PARAM_FNAME(_param)
#define PARAM_PROP_short(_param) App::PropertyInteger PARAM_FNAME(_param)
#define PARAM_PROP_long(_param) App::PropertyInteger PARAM_FNAME(_param)
#define PARAM_PROP_enum(_param) App::PropertyEnumeration PARAM_FNAME(_param)
#define PARAM_PROP_enum2(_param) App::PropertyEnumeration PARAM_FNAME(_param)
/** @} */
/** Helper for #PARAM_PROP_DECLARE */
#define PARAM_PROP_DECLARE_(_param) PARAM_TYPED(PARAM_PROP_, _param)(_param);
/** Declare FreeCAD properties
* \ingroup ParamProperty
*/
#define PARAM_PROP_DECLARE(_seq) PARAM_FOREACH(PARAM_PROP_DECLARE_, _seq)
/** Replace FreeCAD #ADD_PROPERTY_TYPE to fix singifying macro */
#define PARAM_ADD_PROPERTY_TYPE(_prop_, _defaultval_, _group_, _type_, _Docu_) \
do { \
this->_prop_.setValue _defaultval_; \
this->_prop_.setContainer(this); \
propertyData.addProperty( \
static_cast<App::PropertyContainer*>(this), \
BOOST_PP_STRINGIZE(_prop_), &this->_prop_, (_group_), (_type_), (_Docu_) \
); \
} while (0)
/** Generic property adding */
#define PARAM_PROP_ADD_(_group, _param) \
PARAM_ADD_PROPERTY_TYPE( \
PARAM_FNAME(_param), \
(PARAM_FDEF(_param)), \
_group, \
App::Prop_None, \
PARAM_FDOC(_param) \
);
#define PARAM_PROP_ADD_short PARAM_PROP_ADD_
#define PARAM_PROP_ADD_long PARAM_PROP_ADD_
#define PARAM_PROP_ADD_double PARAM_PROP_ADD_
#define PARAM_PROP_ADD_bool PARAM_PROP_ADD_
#define PARAM_PROP_ADD_enum2 PARAM_PROP_ADD_enum
/** Add \c enum type parameter as property */
#define PARAM_PROP_ADD_enum(_group, _param) \
PARAM_ADD_PROPERTY_TYPE( \
PARAM_FNAME(_param), \
((long)PARAM_FDEF(_param)), \
_group, \
App::Prop_None, \
PARAM_FDOC(_param) \
);
/** Helper for #PARAM_PROP_ADD */
#define PARAM_PROP_ADD_TYPED(_1, _group, _i, _param) \
PARAM_TYPED(PARAM_PROP_ADD_, _param)(_group, _param)
/** Add FreeCAD properties
* \ingroup ParamProperty
*/
#define PARAM_PROP_ADD(_group, _seq) BOOST_PP_SEQ_FOR_EACH_I(PARAM_PROP_ADD_TYPED, _group, _seq)
#define PARAM_PROP_SET_ENUM_short(...)
#define PARAM_PROP_SET_ENUM_long(...)
#define PARAM_PROP_SET_ENUM_bool(...)
#define PARAM_PROP_SET_ENUM_double(...)
#define PARAM_PROP_SET_ENUM_enum2 PARAM_PROP_SET_ENUM_enum
/** Setup \c enum type parameter */
#define PARAM_PROP_SET_ENUM_enum(_prefix, _param) \
PARAM_FNAME(_param).setEnums(BOOST_PP_CAT(_prefix, PARAM_FNAME(_param)));
/** Helper for #PARAM_PROP_SET_ENUM */
#define PARAM_PROP_SET_ENUM_TYPED(_1, _prefix, _param) \
PARAM_TYPED(PARAM_PROP_SET_ENUM_, _param)(_prefix, _param)
/* Setup the \c enum string list for \c enum type properties
* \ingroup ParamProperty
*/
#define PARAM_PROP_SET_ENUM(_prefix, _seq) \
BOOST_PP_SEQ_FOR_EACH(PARAM_PROP_SET_ENUM_TYPED, _prefix, _seq)
/** Helper for #PARAM_PROP_ARGS */
#define PARAM_PROP_ARGS_(_i, _param) BOOST_PP_COMMA_IF(_i) PARAM_FNAME(_param).getValue()
/** Expand the property list as function arguments
*
* Expand to:
* \code{.unparsed}
* name1.getValue(), name2.getValue() ...
* \endcode
* \ingroup ParamProperty
*/
#define PARAM_PROP_ARGS(_seq) PARAM_FOREACH_I(PARAM_PROP_ARGS_, _seq)
/** Helper for #PARAM_PROP_TOUCHED */
#define PARAM_PROP_TOUCHED_(_param) \
if (PARAM_FNAME(_param).isTouched()) \
return 1;
/** Returns 1 if any properties is touched
*
* Expand to:
* \code{.unparsed}
* if(name1.isTouched()) return 1;
* if(name2.isTouched()) return 1;
* ...
* \endcode
* \ingroup ParamProperty
*/
#define PARAM_PROP_TOUCHED(_seq) PARAM_FOREACH(PARAM_PROP_TOUCHED_, _seq)
#endif // PARAMS_HELPER_H