// SPDX-License-Identifier: LGPL-2.1-or-later /**************************************************************************** * Copyright (c) 2017 Zheng Lei (realthunder) * * * * 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 /src/Mod/CAM/App * make Area.cpp.i * \endcode * * The preprocessed intermediate output will be at, * \code{.sh} * /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 * a, b, c here cannot directly contain ,. These restriction * is due to the fact that ( ) , 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} * ((, , , , , , )) * \endcode * * - \c type is the type of the parameter. Currently only five types of * parameters are defined, short, long, double, bool, enum, enum2. * \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 (0,()) 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 TYPE, ARG, NAME, DEF, DOC, or SEQ. * 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 #include #include #include #include #include #include #include #include #include #include #include /** \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##\. 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##\ 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 _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## * \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 * _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 * _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 * _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 * _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 * _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(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