Use QuickJS to modify preferences and global variables - quickjs

I am interested in using QuickJS to modify preferences in a C++ app. So, there is a preference variable called
int myPref = 0
I want to expose this as a JS variable called
jsPref
Then the user can modify this directly via JS using
jsPref = 1
This can be extended to a struct - where one can define
struct myPref { int a, int b}; which is exposed as a global in JS as jsPref
Inside the JS script, I wish to call
jsPref.a = 1;
jsPref.b = 2;
and the assignments should be reflected in the C struct myPref
Sadly, I do not even have sample code on exposing a struct. After searching, I only came up with examples of C/C++ functions exposed as JS methods.

Take a look at e.g. JS_AddIntrinsicAtomics in the QuickJS source code. It uses JS_SetPropertyFunctionList to create an object whose properties are defined by a simple array of structures, with the help of some convenience macros.
Instead of JS_CFUNC_MAGIC_DEF, you can use JS_CGETSET_DEF or JS_CGETSET_MAGIC_DEF to define custom properties with getters and setters. Since the contents of the JSContext structure are not exposed in the public API, you will have to use JS_GetGlobalObject to access the global object. Otherwise though, what you need to do is substantially the same.
This should look something like this:
// those will be called when jsPref.a is accessed
static JSValue pref_a_get(JSContext *ctx, JSValueConst this_val);
static JSValue pref_a_set(JSContext *ctx, JSValueConst this_val, JSValueConst value);
// those will be called when jsPref.b or jsPref.c is accessed and receive 123 or 456 respectively in the `magic` parameter
static JSValue pref_bc_get(JSContext *ctx, JSValueConst this_val, int magic);
static JSValue pref_bc_set(JSContext *ctx, JSValueConst this_val, JSValueConst value, int magic);
static const JSCFunctionListEntry prefs_obj_props[] = {
JS_CGETSET_DEF("a", pref_a_get, pref_a_set),
JS_CGETSET_MAGIC_DEF("b", pref_bc_get, pref_bc_set, 123),
JS_CGETSET_MAGIC_DEF("c", pref_bc_get, pref_bc_set, 456),
};
static const JSCFunctionListEntry prefs_obj[] = {
JS_OBJECT_DEF("jsPref", prefs_obj_props, countof(prefs_obj_props), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
};
void create_prefs_obj(JSContext *ctx)
{
JSValue globalThis = JS_GetGlobalObject(ctx);
JS_SetPropertyFunctionList(ctx, globalThis, prefs_obj, countof(prefs_obj));
JS_FreeValue(ctx, globalThis);
}
The above defines the struct variation. For a single preference, you can put a JS_CGETSET_DEF definition directly in the prefs_obj, to define an accessor-based property of the global object.
Note that properties defined using JS_CGETSET_DEF are configurable, which means user code can perform e.g. delete jsPrefs.a; and lose access to the exposed property. If you want to prevent that, you will have to write your own helper macro so that you can set property flags yourself. You may also want to have a look at JS_PreventExtensions, to catch user code attempting to write to a preference that doesn’t exist. If you want to use that, you may need to create the preferences object with JS_NewObject, define its properties with JS_SetPropertyFunctionList, seal it and add it to the global object manually, with JS_DefinePropertyValue, instead of doing everything at once with just one JS_SetPropertyFunctionList call:
void create_prefs_obj(JSContext *ctx)
{
JSValue globalThis = JS_GetGlobalObject(ctx);
// null prototype to avoid namespace clashes
JSValue prefsObj = JS_NewObjectProto(ctx, JS_NULL);
// define preference properties
JS_SetPropertyFunctionList(ctx, prefsObj, prefs_obj, countof(prefs_obj));
// catch user code writing to nonexistent properties
JS_PreventExtensions(ctx, prefsObj);
// expose the preferences object
JS_DefinePropertyValueStr(ctx, globalThis, "jsPref", prefsObj, PROP_CONFIGURABLE | PROP_WRITABLE);
JS_FreeValue(ctx, globalThis);
}

Related

How to implement a property enumerator for a C++ native object in V8?

I'm writing a Node.js C++ module that communicates with a database which returns BSON objects, and wrap them in a V8 object so they can be accessed from the JavaScript.
For that I create an ObjectTemplate, and configure it through SetHandler. My getter and setter work, but the property enumerator does not return anything. Here is the code:
void PropertyEnumerator(const PropertyCallbackInfo<Array>& info)
{
// Enumerator, aka `Object.keys(obj)`
auto isolate = info.GetIsolate();
auto data = unrwap_internal_field<BsonObjectData>(info.Holder(), 0);
Local<Array> array = Array::New(isolate);
int i = 0;
bson_iter_t iter;
bson_iter_init_from_data(&iter, data->document_data, data->document_length);
while (bson_iter_next(&iter)) {
const char* key = bson_iter_key(&iter);
array->Set(i++, String::NewFromUtf8(isolate, key, v8::NewStringType::kNormal).ToLocalChecked());
}
info.GetReturnValue().Set(array);
}
I have checked that the function is actually called
I have checked that the array is populated with the right values
But in the JavaScript when I do console.log(Object.keys(obj)) I get an empty array. It's like info.GetReturnValue().Set(array); does not do anything. The documentation states that each element of the array must be a Name, and String inherits from Name so I really don't understand.
Thoughts?
As #jmrk suggested in their comment, the problem was that I didn't implement the query function for my object template. This made the enumerator work properly:
void PropertyQuery(Local<Name> property, const PropertyCallbackInfo<Integer>& info)
{
info.GetReturnValue().Set(PropertyAttribute::None);
}

C++: How to pass user input through the system without using global variables?

I am having the problem, that my application can has a lot of user input which determines how the application will be run. The application is an in memory database system and the user could for example invoke the program with commands like '--pagesize 16384' (sets the memory page size to use), '--alignment 4096' (sets the memory alignment to use) or '--measure' (sets a flag to measure certain routines).
Currently I save all the user input in global variables which are defined as extern in a header file:
//#file common.hh
extern size_t PAGE_SIZE_GLOBAL;
extern size_t ALIGNMENT_GLOBAL;
extern size_t MEMCHUNK_SIZE_GLOBAL;
extern size_t RUNS_GLOBAL;
extern size_t VECTORIZE_SIZE_GLOBAL;
extern bool MEASURE_GLOBAL;
extern bool PRINT_GLOBAL;
extern const char* PATH_GLOBAL;
and in main source file:
#include "modes.hh"
size_t PAGE_SIZE_GLOBAL;
size_t ALIGNMENT_GLOBAL;
size_t MEMCHUNK_SIZE_GLOBAL;
size_t RUNS_GLOBAL;
size_t VECTORIZE_SIZE_GLOBAL;
bool MEASURE_GLOBAL;
bool PRINT_GLOBAL;
const char* PATH_GLOBAL;
int main(const int argc, const char* argv[]){
...
//Initialize the globals with user input
PAGE_SIZE_GLOBAL = lArgs.pageSize();
ALIGNMENT_GLOBAL = lArgs.alignment();
MEMCHUNK_SIZE_GLOBAL = lArgs.chunkSize();
RUNS_GLOBAL = lArgs.runs();
VECTORIZE_SIZE_GLOBAL = lArgs.vectorized();
MEASURE_GLOBAL = lArgs.measure();
PRINT_GLOBAL = lArgs.print();
std::string tmp = lArgs.path() + storageModel + "/";
PATH_GLOBAL = tmp.c_str();
...
}
I then include the header file common.hh in each file, where a global variable is needed (which can be very deep down in the system).
I already read a dozen times to prevent global variables so this is obviously bad style. In the book 'Code Complete 2' from Steve McConnell the chapter about global variables also stated to prevent global variables and use access routines instead. In the section 'How to Use Access Routines' he writes
"Hide data in a class. Declare that data by using the static keyword
(...) to ensure only a single instance of the data exists. Write
routines that let you look at the data and change it."
First of all, the global data won't change (maybe this is changed later but at least not in the near future). But I don't get how these access routines are any better? I will also have a class I need to include at every file where the data is needed. The only difference is the global data are static members accessed through getter functions.
(Edited) I also thought about using a global data Singleton class. But an object with ALL the global data sounds overkill since only a few global variables of the object are needed at its different destinations.
My Question: Should I just stick to the global variables? Are there better solutions, what am I missing? What are the best practices?
Edit:
If I would identify a few classes where the user input is needed the most, I could change the global data to member variables. What would be the best practice to pass the user input to these classes? Passing the data as parameters through the whole system down to the lowest layers sounds wrong. Is there are design pattern (thinking about something like a factory) which would be suited here?
How to pass user input through the system without using global
variables.
It is easy. Surprise, I created a class.
For a while, I called this class a travel case, because I considered it analogous to the needs of a suitcase during a trip. The TC_t is a non-standard container which held useful things for what is going on at your destination, and there is only one created, with references passed to any other objects that could use the information. Not global, in the strictest sense.
This TC_t is created in main() thread, while studying the command line options.
I recently wrote yet-another-game-of-life. User inputs included a) destination of output (i.e. a tty num), b) initial fill-pattern choices, c) 'overrides' for game board dimensions, d) test modes, including max speed, and vector vs. array options for cell behaviours.
The GOLUtil_t (Game Of Life Utility) (previously TC_t) includes methods that are useful in more than one effort.
For your question, the two typical globals I avoided are the a) gameBoard, and b) ansi terminal access.
std::cout << "accessing '" << aTermPFN << "' with std::ofstream "
<< std::endl;
std::ofstream* ansiTerm = new std::ofstream(aTermPFN);
if (!ansiTerm->is_open())
{
dtbAssert(nullptr != ansiTerm)(aTermPFN);
std::cerr << "Can not access '" << aTermPFN << "'" << std::endl;
assert(0); // abort
}
// create game-board - with a vector of cell*
CellVec_t gameBoard;
gameBoard.reserve (aMaxRow * aMaxCol);
GOLUtil_t gBrd(aMaxRow, aMaxCol, gameBoard, *ansiTerm);
This last line invoked the ctor of GOLUtil_t.
The instance "gBrd" is then passed (by reference) to the ctor of the game, and from there, to any aggregate objects it contained.
std::string retVal;
{
// initialize display, initialize pattern
GameOfLife_t GOL(gBrd, timeOfDay, fillPatternChoiceLetter, useArray);
std::string retValS = GOL.exec2(testMode);
retVal = gBrd.clearGameBoard(retValS); // delete all cells
}
// force GameOfLife_t dtor before close ansiTerm
ansiTerm->close();
Summary - No globals.
Every instance of any class that needed this info (where to output? what are dimensions?) has access to the GOLUtil_t for their entire lifetime. And GOLUtil_t has methods to lighten the coding load.
Note: because single output terminal, I used a single thread (main)
Your first refactor effort might be to:
a) remove the global classes,
b) and instead instantiate these in main() (for lifetime control)
c) and then pass-by-reference these formerly global instances to those non-global objects that make use of them. I recommend in the ctor(s).
d) remember to clean up (delete if new'd)
my environment: Ubuntu 15.10, 64 bit, g++ V5

DLL – static vector which is filled up at DLL’s initialization time, returns zero size to the client program

I am experiencing the following issue, in my DLL project:
At the DLL side :
Inside the DLL I have declared a static vector as follows :
static std::vector<FilterProcessor::FilterInfo*> TableOfContents;
At DLL’s initialization time of static members, I am adding some entries to the above vector.
I have defined an extern “C” global function (getTocPointer()) which is returning a pointer to the vector, when it called from the client program.
extern "C" __declspec(dllexport) std::vector<FilterProcessor::FilterInfo*>* __cdecl getLibraryTOC();
At the client’s program side :
The DLL library is loaded without any problem
The address of getTocPointer() function is returned correctly to the client program, when the getProcAddress() function is called.
Indeed, when I am performing the debugging process in the DLL-side, the client program calls the above function and the execution process enters to it.
However, the vector has a zero size and, has no any contents which were added to it at initialization time. It seems it points to another vector object. . .
I can’t really understand what exactly goes wrong here.
The way of adding entries to this vector at initialization time, is the proper way?
If yes, what probably goes wrong when the client program calls the getLibraryTOC() function?
Thanks in advance
George
If that static global definition of the vector appears in a header file, then yes you do have multiple different vectors. Change the keyword static to extern to make the header file declare the vector rather than defining it, and then add exactly one definition in an implementation file.
Then, you may encounter the static initialization order fiasco. If the vector is defined in a different compilation unit than the code attempting to add entries to it, there's no guarantee that the vector object is alive yet. Attempting to use a vector whose constructor hasn't run is undefined behavior -- it might easily manifest as the constructor running afterward and setting the contents to zero length (as a default constructor should), but many other problems are possible.
You can avoid the SIOF by using a local static.
std::vector<FilterProcessor::FilterInfo*>& table_of_contents()
{
static std::vector<FilterProcessor::FilterInfo*> singleton;
return singleton;
}
In every location that would have accessed the global, including the initialization logic that fills the vector, and also your getLibraryTOC() exported function, call the accessor function instead.
That all is applicable to any C++ software having multiple compilation units. When you have a DLL, things get even more complicated, because the DLL and EXE are compiled and linked separately from each other, possibly with different settings, different compilers, or even entirely different languages. Sharing of complex objects across DLL boundaries is real trouble. If the DLL and EXE are always recompiled at the same time, it can work. But if you're trying to distribute the DLL for use by another party who writes the EXE code, the strong coupling will quickly become intolerable.
A better approach is to hide the library objects from the DLL boundary, and pass only primitive or OS-managed types across. For example:
#define DLLAPI __declspec(dllexport) __cdecl
extern "C" DLLAPI int32_t getLibraryTocCount()
{ return table_of_contents.size(); }
extern "C" DLLAPI BSTR getLibraryTocName(int index)
{ return ::SysAllocString(table_of_contents[index].name.c_str(); } // assumes std::wstring
// etc
The library I have implemented contains the following code (in a brief description) :
An Index class which implements the Table of contents of the library
A collection of audio filters named Filter01, Filter02 etc.
Index.h
struct LIB_SPECS Library_TableOfContents
{
static bool addTOCEntry(FilterInfo* Filter_Info); // add an entry to the TOC
static std::vector<FilterInfo*> TableOfContents; // TOC
};
/*-------------------------------------------------------------------
Called from the client program to return the pointer to TOC */
extern "C" LIB_SPECS std::vector<FlterInfo*>* __cdecl getLibraryTOC();
Index.cpp
/* Define / Initialize static variables */
std::vector<FilterInfo*> Library_TableOfContents::TableOfContents = {};
//=====================================================================
bool Library_TableOfContents::addTOCEntry(FilterInfo* Filter_Info)
{
Library_TableOfContents::TableOfContents.push_back(Filter_Info);
return false;
}
//======================================================================
std::vector<FilterInfo*>* getLibraryTOC()
{
return &Library_TableOfContents::TableOfContents;
}
For each Audio Filter in the library :
Filterxx.h
class LIB_SPECS Filterxx
{
public:
static struct FilterInfo
{
public:
std::string filterName;
std::string filterDescription;
// other filter info
FilterInfo(); // FilterInfo constructor
} Filter_Info;
virtual String doSomeWork(int AvatarId);
virtual void deleteFilter() = 0;
};
Filterxx.cpp
Filterxx::FilterInfo Filterxx::Filter_Info("Filterxx", “A filter description e.g. Low pass Filter ” ); //
FilterInfo::FilterInfo(std::string name, std::string description)
{
Filter_Info.filterName = name;
Filter_Info.filterDescription = description;
Library_TableOfContents::addTOCEntry(&Filter_Info);
}
// other filter functions
The getLibraryTOC() function, is called from the client program to get the table of contents in order to show it to the user.
As I said, indeed it is called by the client but, at the time of call, the table of contents seems to have a zero size.

How do I load custom "const" variables from a config file in C++?

I currently have a function which loads variables from a config file. It uses these to initialise a set of constant config variables.
// header file
namespace cfg {
extern const char *config_value;
}
// source file
namespace cfg {
const char *config_value;
}
bool load_config() {
cfg::config_value = load_config_line("config_value");
}
const char *load_config_line(const char *key) {
// read value from config.cfg...
}
This works pretty well. The problem is that now I want to reuse this code in several other projects, which means the constant values have to change. This means changing the config_value names in four different places each in the code. It also means I have several copies of essentially the same code to maintain in different projects.
Is there a way of setting different sets of constant values using the same code for the reading and parsing? Perhaps so that all I have to do is change the header file and it automatically finds those value names in the config file? The tricky part is that ideally the outward facing config values themselves should be constant and available at compile time (using a string to value map for example is undesirable, as I would prefer to have the compile time protection).
The solution here is to not use global variables, and instead have some settings struct which you explicitly initialize with values loaded from the file. The struct instance itself doesn't need to be const (you'll need to be able to load the values into it, unless you pass everything in on construction), but all access to it should be const. This last bit can be achieved by passing settings as e.g. a const settings& to wherever it is needed.
int main()
{
// Variant A: 2-step init
settings s;
s.loadConfigFile(filename);
// Variant B: 1-step init - can make instance itself const
const settings s(filename);
Thing worker(s); // Thing::Thing(const settings&)
worker.work();
}
Of course Worker can be anything your heart desires.
Note that settings itself needs no special constness whatsoever:
struct settings
{
std::string config_value;
}
It is the external const that guards access to the values contained within.

Initialize global array of function pointers at either compile-time, or run-time before main()

I'm trying to initialize a global array of function pointers at compile-time, in either C or C++. Something like this:
module.h
typedef int16_t (*myfunc_t)(void);
extern myfunc_array[];
module.cpp
#include "module.h"
int16_t myfunc_1();
int16_t myfunc_2();
...
int16_t myfunc_N();
// the ordering of functions is not that important
myfunc_array[] = { myfunc_1, myfunc_2, ... , myfunc_N };
func1.cpp, func2.cpp, ... funcN.cpp (symbolic links to a single func.cpp file, so that different object files are created: func1.o, func2.o, func3.o, ... , funcN.o. NUMBER is defined using g++ -DNUMBER=N)
#include "module.h"
#define CONCAT2(x, y) x ## y
#define CONCAT(x, y) CONCAT2(x, y)
int16_t CONCAT(myfunc_, NUMBER)() { ... }
When compiled using g++ -DNUMBER=N, after preprocessing becomes:
func1.cpp
...
int16_t myfunc_1() { ... }
func2.cpp
...
int16_t myfunc_2() { ... }
and so on.
The declarations of myfunc_N() and the initialization of myfunc_array[] are not cool, since N changes often and could be between 10 to 200. I prefer not to use a script or Makefile to generate them either. The ordering of functions is not that important, i can work around that. Is there a neater/smarter way to do this?
How To Make a Low-Level Function Registry
First you create a macro to place pointers to your functions in a special section:
/* original typedef from question: */
typedef int16_t (*myfunc)(void);
#define myfunc_register(N) \
static myfunc registered_##myfunc_##N \
__attribute__((__section__(".myfunc_registry"))) = myfunc_##N
The static variable name is arbitrary (it will never be used) but it's nice to choose an expressive name. You use it by placing the registration just below your function:
myfunc_register(NUMBER);
Now when you compile your file (each time) it will have a pointer to your function in the section .myfunc_registry. This will all compile as-is but it won't do you any good without a linker script. Thanks to caf for pointing out the relatively new INSERT AFTER feature:
SECTIONS
{
.rel.rodata.myfunc_registry : {
PROVIDE(myfunc_registry_start = .);
*(.myfunc_registry)
PROVIDE(myfunc_registry_end = .);
}
}
INSERT AFTER .text;
The hardest part of this scheme is creating the entire linker script: You need to embed that snippet in the actual linker script for your host which is probably only available by building binutils by hand and examining the compile tree or via strings ld. It's a shame because I quite like linker script tricks.
Link with gcc -Wl,-Tlinkerscript.ld ... The -T option will enhance (rather than replace) the existing linker script.
Now the linker will gather all of your pointers with the section attribute together and helpfully provide a symbol pointing before and after your list:
extern myfunc myfunc_registry_start[], myfunc_registry_end[];
Now you can access your array:
/* this cannot be static because it is not know at compile time */
size_t myfunc_registry_size = (myfunc_registry_end - myfunc_registry_start);
int i;
for (i = 0; i < myfunc_registry_size); ++i)
(*myfunc_registry_start[i])();
They will not be in any particular order. You could number them by putting them in __section__(".myfunc_registry." #N) and then in the linker gathering *(.myfunc_registry.*), but the sorting would be lexographic instead of numeric.
I have tested this out with gcc 4.3.0 (although the gcc parts have been available for a long time) and ld 2.18.50 (you need a fairly recent ld for the INSERT AFTER magic).
This is very similar to the way the compiler and linker conspire to execute your global ctors, so it would be a whole lot easier to use a static C++ class constructor to register your functions and vastly more portable.
You can find examples of this in the Linux kernel, for example __initcall is very similar to this.
I was going to suggest this question is more about C, but on second thoughts, what you want is a global container of function pointers, and to register available functions into it. I believe this is called a Singleton (shudder).
You could make myfunc_array a vector, or wrap up a C equivalent, and provide a function to push myfuncs into it. Now finally, you can create a class (again you can do this in C), that takes a myfunc and pushes it into the global array. This will all occur immediately prior to main being called. Here are some code snippets to get you thinking:
// a header
extern vector<myfunc> myfunc_array;
struct _register_myfunc {
_register_myfunc(myfunc lolz0rs) {
myfunc_array.push_back(lolz0rs);
}
}
#define register_myfunc(lolz0rs) static _register_myfunc _unique_name(lolz0rs);
// a source
vector<myfunc> myfunc_array;
// another source
int16_t myfunc_1() { ... }
register_myfunc(myfunc_1);
// another source
int16_t myfunc_2() { ... }
register_myfunc(myfunc_2);
Keep in mind the following:
You can control the order the functions are registered by manipulating your link step.
The initialization of your translation unit-scoped variables occurs before main is called, i.e. the registering will be completed.
You can generate unique names using some macro magic and __COUNTER__. There may be other sneaky ways that I don't know about. See these useful questions:
Unnamed parameters in C
Unexpected predefined macro behaviour when pasting tokens
How to generate random variable names in C++ using macros?
Your solution sounds much too complicated and error prone to me.
You go over your project with a script (or probably make) to place the -D options to the compiler, anyhow. So I suppose you are keeping a list of all your functions (resp. the files defining them).
I'd use proper names for all the functions, nothing of your numbering scheme and then I would produce the file "module.cpp" with that script and initialize the table with the names.
For this you just have to keep a list of all your functions (and perhaps filenames) in one place. This could be easier be kept consistent than your actual scheme, I think.
Edit: Thinking of it even this might also be overengineering. If you have to maintain a list of your functions somewhere in any case, why not just inside the file "module.cpp"? Just include all the header files of all your functions, there, and list them in the initializer of the table.
Since you allow C++, the answer is obviously yes, with templates:
template<int N> int16_t myfunc() { /* N is a const int here */ }
myfunc_array[] = { myfunc<0>, myfunc<1>, myfunc<2> }
Now, you might wonder if you can create that variable-length initializer list with some macro. The answer is yes, but the macro's needed are ugly. So I'n not going to write them here, but point you to Boost::Preprocessor
However, do you really need such an array? Do you really need the name myfunc_array[0] for myfunc<0> ? Even if you need a runtime argument (myfunc_array[i]) there are other tricks:
inline template <int Nmax> int16_t myfunc_wrapper(int i) {
assert (i<Nmax);
return (i==Nmax) ? myfunc<Nmax> : myfunc_wrapper(i-1);
}
inline int16_t myfunc_wrapper(int i) {
return myfunc_wrapper<NUMBER>(i); // NUMBER is defined on with g++ -DNUMBER=N
}
Ok I worked out a solution based on Matt Joiner's tip:
module.h
typedef int16_t (*myfunc_t)(void);
extern myfunc_array[];
class FunctionRegistrar {
public:
FunctionRegistrar(myfunc_t fn, int fn_number) {
myfunc_array[fn_number - 1] = fn; // ensures correct ordering of functions (not that important though)
}
}
module.cpp
#include "module.h"
myfunc_array[100]; // The size needs to be #defined by the compiler, probably
func1.cpp, func2.cpp, ... funcN.cpp
#include "module.h"
static int16_t myfunc(void) { ... }
static FunctionRegistrar functionRegistrar(myfunc, NUMBER);
Thanks everyone!