source: cpp/frams/param/param.h @ 1315

Last change on this file since 1315 was 1315, checked in by Maciej Komosinski, 4 months ago

Offset of fields within an object: replaced subtraction of pointers/addresses with the official offsetof()

  • Property svn:eol-style set to native
File size: 16.5 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[1315]2// Copyright (C) 1999-2024  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[109]4
5#ifndef _PARAM_H_
6#define _PARAM_H_
7
8#include <stdio.h>
[247]9#include <stdint.h>
[1184]10#include <vector>
[109]11#include <frams/util/sstring.h>
[784]12#include <frams/util/sstringutils.h>
[109]13#include <frams/util/list.h>
14#include <frams/util/statrick.h>
[382]15#include <common/virtfile/virtfile.h>
[375]16#include <common/log.h>
[109]17
18class ExtValue;
19class ExtObject;
20
21// ParamInterface flags:
[316]22#define PARAM_READONLY       1  //< Param is not able to change this member
23#define PARAM_DONTSAVE       2  //< Param will not save this member
24#define PARAM_SETLEVEL(x) (((x)&3)<<2)  //< internal use
25#define PARAM_LEVEL(x)    (((x)>>2)&3)  //< internal use
26#define PARAM_USERREADONLY  16  //< GUI should not change this member (even if Param can)
27#define PARAM_USERHIDDEN    32  //< GUI should not display this member
28#define MUTPARAM_ALLOCENTRY 64  //< for mutableparam (private!)
29#define MUTPARAM_ALLOCDATA 128  //< for mutableparam (private!)
30#define PARAM_NOSTATIC  256     //< (FramScript) don't access this member in a static object (ClassName.field)
31#define PARAM_CONST     512     //< (FramScript) constant value
[720]32#define PARAM_CANOMITNAME 1024  //< affects Param::saveSingleLine()/loadSingleLine() - for example one-liners in f0 genetic encoding
[316]33#define PARAM_DONTLOAD    2048  //< Param::load() skips this field
34#define PARAM_NOISOLATION 4096  //< don't use proxy object in master/slave interactions
35#define PARAM_DEPRECATED  8192  //< this member is deprecated
[333]36#define PARAM_LINECOMMENT 16384 //< Param::load() adds "@line ..." comment when loading multiline (internal use)
[478]37#define PARAM_OBJECTSET 32768   //< setting this field is handled by the object's assign(...) function and cannot change the object reference
[109]38
[247]39typedef int32_t paInt;
40#define PA_INT_SCANF "%d"
[284]41#define PA_INT_SCANF_X "%x"
[247]42
[154]43// the result of param::set() is a combination of bits:
[109]44
[154]45// read-only: cannot modify
[109]46#define PSET_RONLY      1
47
[154]48// value has been modified
[109]49#define PSET_CHANGED    2
50
[154]51//value has been adjusted because it tried to exceed min or max
[109]52#define PSET_HITMIN     4
53#define PSET_HITMAX     8
54
[393]55#define PSET_NOPROPERTY 16
56
57#define PSET_PARSEFAILED        32
58
[154]59// useful combination: need to get and display the value so that a user knows that the value they tried to set has been rejected or changed
[393]60#define PSET_WARN (PSET_RONLY | PSET_HITMIN | PSET_HITMAX | PSET_PARSEFAILED)
[109]61
62
63struct ParamEntry;
64
65/** Property get/set interface - runtime access to named properties */
66class ParamInterface
67{
68public:
[319]69        virtual ~ParamInterface() {}
70
[154]71        virtual int getGroupCount() = 0; ///< @return the number of property groups
72        virtual int getPropCount() = 0; ///< @return the number of properties
[109]73
[154]74        virtual const char* getName() = 0;
[950]75        virtual const char* getLongName() { return getName(); }
[154]76        virtual const char* getDescription() { return 0; }
77        virtual ParamEntry *getParamTab() const { return NULL; }
[109]78
[154]79        int findId(const char *n);      ///< find id number for internal name
80        int findIdn(const char *naz, int n);
[1155]81        int findGroupId(const char* name);
[109]82
[154]83        virtual const char *id(int i) = 0;      ///< get internal name
84        virtual const char *name(int i) = 0;    ///< get the human-readable name
[109]85
[154]86        /** get type description.
87                first character defines basic datatype:
88                - d = integer
89                - f = floating point
90                - s = string
91                - o = ExtObject
92                - x = ExtValue (universal datatype)
93                */
94        virtual const char *type(int i) = 0;
[109]95
[154]96        virtual const char *help(int i) = 0;    ///< get long description (tooltip)
[109]97
[154]98        virtual int flags(int i) = 0;           ///< get flags
[109]99
[154]100        virtual int group(int i) = 0;           ///< get group id for a property
101        virtual const char *grname(int gi) = 0; ///< get group name
102        virtual int grmember(int gi, int n) = 0;        ///< get property id for n'th member of group "gi"
[109]103
[154]104        virtual void call(int i, ExtValue* args, ExtValue *ret) = 0;
[109]105
[154]106        void get(int, ExtValue &retval);        ///< most universal get, can be used for every datatype
[109]107
[154]108        virtual SString getString(int) = 0;     ///< get string value, you can only use this for "s" type property
[247]109        virtual paInt getInt(int) = 0;  ///< get long value, you can only use this for "d" type property
[154]110        virtual double getDouble(int) = 0;      ///< get double value, you can only use this for "f" type property
111        virtual ExtObject getObject(int) = 0;   ///< get object reference, you can only use this for "o" type property
112        virtual ExtValue getExtValue(int) = 0;  ///< get extvalue object, you can only use this for "x" type property
[109]113
[154]114        SString get(int);               ///< old style get, can convert long or double to string
115        SString getText(int);           ///< like getString, returns enumeration label for subtype "d 0 n ~enum1~enum2...
[109]116
[154]117        SString getStringById(const char*prop);  ///< get string value, you can only use this for "s" type property
[247]118        paInt getIntById(const char* prop);    ///< get long value, you can only use this for "d" type property
[154]119        double getDoubleById(const char* prop);///< get double value, you can only use this for "f" type property
120        ExtObject getObjectById(const char* prop);///< get object reference, you can only use this for "o" type property
121        ExtValue getExtValueById(const char* prop);///< get extvalue object, you can only use this for "x" type property
122        ExtValue getById(const char* prop);
[109]123
[743]124        int setIntFromString(int i, const char* str, bool strict = false);
125        int setDoubleFromString(int i, const char* str);
[247]126        virtual int setInt(int, paInt) = 0;             ///< set long value, you can only use this for "d" type prop
[154]127        virtual int setDouble(int, double) = 0; ///< set double value, you can only use this for "f" type prop
128        virtual int setString(int, const SString &) = 0;        ///< set string value, you can only use this for "s" type prop
129        virtual int setObject(int, const ExtObject &) = 0;      ///< set object reference, you can only use this for "o" type prop
130        virtual int setExtValue(int, const ExtValue &) = 0;     ///< 4 in 1
[109]131
[154]132        int set(int, const ExtValue &);///< most universal set, can be used for every datatype
[109]133
[743]134        int setFromString(int, const char*, bool strict = false);               ///< oldstyle set, can convert string to long or double
[109]135
[247]136        int setIntById(const char* prop, paInt);///< set long value, you can only use this for "d" type prop
[154]137        int setDoubleById(const char* prop, double);///< set double value, you can only use this for "f" type prop
138        int setStringById(const char* prop, const SString &);///< set string value, you can only use this for "s" type prop
139        int setObjectById(const char* prop, const ExtObject &);///< set object reference, you can only use this for "o" type prop
140        int setExtValueById(const char* prop, const ExtValue &); ///< for ExtValue types only
141        int setById(const char* prop, const ExtValue &);///< can be used for all property types
[109]142
[154]143        /** get valid minimum, maximum and default value for property 'prop'
144                @return 0 if min/max/def information is not available */
[743]145        int getMinMaxInt(int prop, paInt& minumum, paInt& maximum, paInt& def);
[154]146        /** get valid minimum, maximum and default value for property 'prop'
147                @return 0 if min/max/def information is not available */
[743]148        int getMinMaxDouble(int prop, double& minumum, double& maximum, double& def);
149        int getMinMaxString(int prop, int& minumum, int& maximum, SString& def);
150        static int getMinMaxIntFromTypeDef(const char* type, paInt& minumum, paInt& maximum, paInt& def);
151        static int getMinMaxDoubleFromTypeDef(const char* type, double& minumum, double& maximum, double& def);
152        static int getMinMaxStringFromTypeDef(const char* type, int& minumum, int& maximum, SString& def);
[109]153
[278]154        virtual void setDefault();
155        virtual void setDefault(int i);
[154]156        void setMin();
157        void setMax();
158        void setMin(int i);
159        void setMax(int i);
[109]160
[640]161        /** return the human readable description of the given type */
[743]162        static SString friendlyTypeDescrFromTypeDef(const char* type);
163        SString friendlyTypeDescr(int i) { return friendlyTypeDescrFromTypeDef(type(i)); }
[650]164
[154]165        /** copy all property values from other ParamInterface object */
166        void copyFrom(ParamInterface *src);
[109]167
[154]168        /** Copy all property values from compatible ParamInterface object.
169                This method is more efficient than copyFrom,
170                but can be used only if the other object has the same properties sequence, e.g.:
171                - any two Param objects having common paramtab
172                - any ParamInterface object and the Param with paramtab constructed by ParamObject::makeParamTab
173                */
174        void quickCopyFrom(ParamInterface *src);
[109]175
[720]176        enum FileFormat { FormatMultiLine, FormatSingleLine }; // FormatJSON in the future?
177        struct LoadOptions {
178                bool warn_unknown_fields; bool *abortable; int *linenum; int offset; bool parse_failed;
179                LoadOptions() : warn_unknown_fields(false), abortable(NULL), linenum(NULL), offset(0), parse_failed(false) {}
180        };
181
[745]182        int saveMultiLine(VirtFILE*, const char* altname = NULL, bool force = 0);
[154]183        int saveprop(VirtFILE*, int i, const char* p, bool force = 0);
[109]184
[720]185        int load(FileFormat format, VirtFILE*, LoadOptions *load_options = NULL);///< @return the number of fields loaded
186        int load(FileFormat format, const SString &, LoadOptions *load_options = NULL);///< @return the number of fields loaded
187protected:
188        int loadMultiLine(VirtFILE*, LoadOptions &options);///< @return the number of fields loaded
189        int loadSingleLine(const SString &, LoadOptions &options);///< @return the number of fields loaded
190public:
191
[154]192        static const char* SERIALIZATION_PREFIX;
[230]193
[535]194        static bool isValidTypeDescription(const char* t);
[483]195#ifdef _DEBUG
[230]196        virtual void sanityCheck(int i) {}
197#endif
[950]198        SString nameDotProperty(int prop);
199        SString nameDotPropertyForMessages(int prop);
[109]200};
201
[1184]202
203
204/** Copy fields between two compatible params. A Param2ParamCopy object can be static so the field names are only looked up once. */
205class Param2ParamCopy
206{
207        std::vector<int> fields;
208public:
209        Param2ParamCopy(ParamInterface& schema, const std::initializer_list<const char*> names);
210        void operator()(const ParamInterface& from, ParamInterface& to);
211};
212
213
214
215
[109]216// implementations:
217
218#define PROCOFFSET(_proc_) ( (void (*)(void*,ExtValue*,ExtValue*)) &(FIELDSTRUCT :: _proc_ ## _statrick))
219#define STATICPROCOFFSET(_proc_) ( (void (*)(void*,ExtValue*,ExtValue*)) &(FIELDSTRUCT :: _proc_))
220#define GETOFFSET(_proc_) ( (void (*)(void*,ExtValue*)) &(FIELDSTRUCT :: _proc_ ## _statrick))
221#define SETOFFSET(_proc_) ( (int (*)(void*,const ExtValue*)) &(FIELDSTRUCT :: _proc_ ## _statrick))
222
[1315]223#define FIELDOFFSET(_fld_) ((intptr_t)offsetof(FIELDSTRUCT, _fld_)) // https://stackoverflow.com/questions/177885/looking-for-something-similar-to-offsetof-for-non-pod-types , https://stackoverflow.com/questions/713963/why-does-this-implementation-of-offsetof-work
[109]224
[483]225#ifdef _DEBUG
[247]226#define PARAM_ILLEGAL_OFFSET ((intptr_t)0xdeadbeef)
[230]227#else
228#define PARAM_ILLEGAL_OFFSET 0
229#endif
230
[109]231#define FIELD(_fld_) FIELDOFFSET(_fld_),0,0
232#define LONGOFFSET(_o_) (_o_),0,0
[230]233#define PROCEDURE(_proc_) PARAM_ILLEGAL_OFFSET,(void*)PROCOFFSET(_proc_),0
234#define STATICPROCEDURE(_proc_) PARAM_ILLEGAL_OFFSET,(void*)STATICPROCOFFSET(_proc_),0
235#define GETSET(_proc_) PARAM_ILLEGAL_OFFSET,(void*)GETOFFSET(get_ ## _proc_),(void*)SETOFFSET(set_ ## _proc_)
[109]236#define GETFIELD(_proc_) FIELDOFFSET(_proc_),(void*)GETOFFSET(get_ ## _proc_),0
237#define SETFIELD(_proc_) FIELDOFFSET(_proc_),0,(void*)SETOFFSET(set_ ## _proc_)
[230]238#define GETONLY(_proc_) PARAM_ILLEGAL_OFFSET,(void*)GETOFFSET(get_ ## _proc_),0
239#define SETONLY(_proc_) PARAM_ILLEGAL_OFFSET,0,(void*)SETOFFSET(set_ ## _proc_)
[109]240
241#define PARAMPROCARGS ExtValue* args,ExtValue* ret
242#define PARAMSETARGS const ExtValue* arg
243#define PARAMGETARGS ExtValue* ret
244
245#define PARAMPROCDEF(name) STATRICKDEF2(name,ExtValue*,ExtValue*)
246#define PARAMGETDEF(name) STATRICKDEF1(get_ ## name,ExtValue*)
247#define PARAMSETDEF(name) STATRICKRDEF1(int,set_ ## name,const ExtValue*)
248
249///////////////////////////////
250
251struct ParamEntry
252{
[154]253        const char *id;
[478]254        paInt group, flags;
[154]255        const char *name, *type;
[247]256        intptr_t offset;
[154]257        void *fun1; ///< procedure or get
258        void *fun2; ///< set
259        const char *help;
[109]260};
261
[154]262struct ParamEntryConstructor : public ParamEntry
[109]263{
264public:
[478]265        ParamEntryConstructor(const char *_id, paInt _group = 0, paInt _flags = 0, const char *_name = 0, const char *_type = 0, intptr_t _offset = 0, void *_fun1 = 0, void *_fun2 = 0, const char *_help = 0)
[154]266        {
267                id = _id; group = _group; flags = _flags; name = _name; type = _type; offset = _offset; fun1 = _fun1; fun2 = _fun2; help = _help;
268        }
[109]269};
270
[154]271class SimpleAbstractParam : public virtual ParamInterface
[109]272{
273protected:
[154]274        virtual void *getTarget(int i);
275        const char* myname;
[950]276        const char* mylongname;
[154]277        bool dontcheckchanges;
[109]278
279public:
[154]280        void *object;
[109]281
[154]282        const char* getName() { return myname; }
[950]283        const char* getLongName() { return mylongname ? mylongname : myname; }
284
[154]285        void setName(const char* n) { myname = n; }
[950]286        void setLongName(const char* n) { mylongname = n; }
[109]287
[154]288        /**
289                @param t ParamEntry table
290                @param o controlled object
291                @param n Param's name
292                */
[950]293        SimpleAbstractParam(void* o = 0, const char*n = 0) :myname(n), mylongname(NULL), dontcheckchanges(0), object(o) {}
[154]294        void setDontCheckChanges(bool x) { dontcheckchanges = x; }
[109]295
[154]296        void select(void *o) { object = o; }
297        void* getSelected() { return object; }
[109]298
[253]299        virtual ParamEntry *entry(int i) = 0;
[154]300        const char *id(int i) { return (i >= getPropCount()) ? 0 : entry(i)->id; }
301        const char *name(int i) { return entry(i)->name; }
302        const char *type(int i) { return entry(i)->type; }
303        const char *help(int i) { return entry(i)->help; }
304        int flags(int i) { return entry(i)->flags; }
305        int group(int i) { return entry(i)->group; }
306        void call(int i, ExtValue* args, ExtValue *ret);
[109]307
[154]308        SString getString(int);
[247]309        paInt getInt(int);
[154]310        double getDouble(int);
311        ExtObject getObject(int);
312        ExtValue getExtValue(int);
[109]313
[805]314        void messageOnExceedRange(int i, int setflags, ExtValue& valuetoset);
[247]315        int setInt(int, paInt);
[154]316        int setDouble(int, double);
317        int setString(int, const SString &);
318        int setObject(int, const ExtObject &);
319        int setExtValue(int, const ExtValue &);
[109]320
[154]321        int isequal(int i, void* defdata);
[720]322        void saveSingleLine(SString&, void *defdata, bool addcr = true, bool all_names = true);
[109]323
[278]324        virtual void setDefault();
325        virtual void setDefault(int i);
[230]326
[483]327#ifdef _DEBUG
[230]328        void sanityCheck(int i);
329#endif
[109]330};
331
[1184]332#ifdef _DEBUG
333#define CHECK_PARAMENTRY_COUNT // This could be the only version, because it is not significantly more costly than the other one. Now we have a _DEBUG (checking, slightly slower) version and a non-_DEBUG (i.e. release, non-checking, slightly faster) version. Since both versions are completely different, the non-_DEBUG (release) version is not verified at all during thorough _DEBUG testing.
334#endif
335
[154]336class Param : public SimpleAbstractParam
[109]337{
338protected:
[154]339        ParamEntry *entry(int i) { return tab + tab[0].group + i; }
[109]340public:
[154]341        ParamEntry *tab;
342        /**
343                @param t ParamEntry table
344                @param o controlled object
345                @param n Param's name
346                */
[109]347
[1184]348#ifdef CHECK_PARAMENTRY_COUNT
[1315]349
[1184]350        template <int COUNT>
[1315]351        Param(ParamEntry(&t)[COUNT], void* o = 0, const char*n = 0) :SimpleAbstractParam(o, n), tab(t)
[1184]352        {
[1315]353                if (!n && tab) myname = tab[0].name;
[1184]354                //printf("Param(ParamEntry t[%d]) %s\n",COUNT,myname?myname:"unknown name");
355                if (tab)
[1315]356                {
357                        int in_array = COUNT - 1 - tab[0].group;
[1184]358                        if (tab[0].flags != in_array)
[1315]359                                printf("\nCHECK_PARAMENTRY_COUNT: %d items in ParamEntry[] array, declared %d (%s)\n\n", in_array, tab[0].flags, myname ? myname : "unknown name");
360                        if (in_array > 0 && tab[COUNT - 1].id != NULL) //in_array>0 -> completely empty paramtab can't end with the usual zero row because that's also its first row
361                                printf("\nCHECK_PARAMENTRY_COUNT: last entry is not null (%s)\n\n", myname ? myname : "unknown name");
362                }
[1184]363        }
364
[1315]365        template<typename T, typename std::enable_if_t<std::is_same<ParamEntry*, T>::value>* = nullptr > //SFINAE-fu because the normal ParamEntry* overload would be also called for ParamEntry[COUNT] argument
[1184]366        Param(T t, void* o = 0, const char*n = 0) :SimpleAbstractParam(o, n), tab(t)
367        {
368                //printf("Param(ParamEntry* t) %s\n",tab?tab[0].name:"");
[1315]369                if (!n && tab) myname = tab[0].name;
[1184]370        }
371
372        Param() :SimpleAbstractParam(NULL, NULL), tab(NULL)
373        {
374                //printf("Param(NULL)\n");
375        }
376
377#else // CHECK_PARAMENTRY_COUNT
[1315]378
379        Param(ParamEntry *t = 0, void* o = 0, const char*n = 0) : SimpleAbstractParam(o, n), tab(t)
[154]380        {
[1315]381                if (!n && tab) myname = tab[0].name;
[154]382        }
[1315]383
[1184]384#endif // CHECK_PARAMENTRY_COUNT
[1315]385
[154]386        Param(const Param& p) :SimpleAbstractParam(p.object, p.myname), tab(p.tab) {}
387        void operator=(const Param&p) { object = p.object; myname = p.myname; tab = p.tab; }
[109]388
[154]389        const char* getDescription() { return tab[0].type; }
[950]390        const char* getLongName() { return tab[0].help ? tab[0].help : SimpleAbstractParam::getLongName(); }
[109]391
[154]392        int getGroupCount() { return tab[0].group; }
393        int getPropCount() { return tab[0].flags; }
394        const char *grname(int i) { return (i < getGroupCount()) ? tab[i].id : 0; }
395        int grmember(int, int);
396        void setParamTab(ParamEntry *t, int dontupdatename = 0) { tab = t; if ((!dontupdatename) && tab) myname = tab[0].name; }
397        ParamEntry *getParamTab() const { return tab; }
[109]398};
399
400extern ParamEntry empty_paramtab[];
401
402#endif
Note: See TracBrowser for help on using the repository browser.