I have the following scenario in a program that was written in C ++ (Arduino):
Service.h
class Service
{
public:
Service(AsyncWebServer *server, FS *fs);
~Service();
void loop();
private:
AsyncWebServer *_server;
FS *_fs;
JsonObject *Settings;
void LoadSettings();
}
Service.cpp
Service::Service(AsyncWebServer *server, FS *fs) : _server(server), _fs(fs) { }
Service::LoadSettings(){
{
File configFile = _fs->open("data.json", "r");
DynamicJsonBuffer jsonBuffer;
JsonObject &root = jsonBuffer.parseObject(configFile);
Settings = &root;
configFile.close();
}
Service:loop() {
LoadSettings();
}
In a correct program, once the Settings property is set, it should hold the value throughout the lifecycle, however I need to call the LoadSettings() method all the time because the Settings property is losing the value when the program exits the method LoadSettings()
What is the right way to work with pointers and class properties? What do i need to do?
The thing what you are doing is creating a local object by parsing the object file and then leaving the function
DynamicJsonBuffer jsonBuffer; // declared locally
JsonObject& root = jsonBuffer.parseObject(configFile) // root local
creates root, a local reference, then you set Settings to point to address of that reference after leaving the function, jsonBuffer ceases to exist.
You should instead either declare jsonBuffer static or better, make it a member variable _jsonBuffer
Not sure why you need root, just write
Settings = &(_jsonBuffer.parseObject(configFile));
(maybe you should also rename Settings to _settings to have the same style as the other member variables).
Related
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);
}
Ok first off I'm very new to C++ so apologies if my understanding is poor. I'll try explain myself as best I can. What I have is I am using a library function that returns a std::shared_ptr<SomeObject>, I then have a different library function that takes a raw pointer argument (more specifically node-addon-api Napi::External<T>::New(Napi::Env env, T *data) static function). I want to create a Napi::External object using my std::shared_ptr. What I am currently doing is this:
{
// ...
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(info.Env(), pSomeObject.get());
auto instance = MyNapiObjectWrapper::Create({ ext });
return instance;
}
But I am worried this will run into memory issues.
My pSomeObject only exists in the current scope, so I imagine what should happen is after the return, it's reference count will drop to 0 and the SomeObject instance it points to will be destroyed and as such I will have issues with the instance I return which uses this object. However I have been able to run this code and call functions on SomeObject from my instance, so I'm thinking maybe my understanding is wrong.
My question is what should I do when given a shared pointer but I need to work off a raw pointer because of other third party library requirements? One option that was proposed to me was make a deep copy of the object and create a pointer to that
If my understanding on any of this is wrong please correct me, as I said I'm quite new to C++.
============================
Edit:
So I was missing from my original post info about ownership and what exactly this block is. The block is an instance method for an implementation I have for a Napi::ObjectWrap instance. This instance method needs to return an Napi::Object which will be available to the caller in node.js. I am using Napi::External as I need to pass a sub type of Napi::Value to the constructor New function when creating the Napi:Object I return, and I need the wrapped SomeObject object in the external which I extract in my MyNapiObjectWrapper constructor like so:
class MyNapiObjectWrapper
{
private:
SomeObject* someObject;
static Napi::FunctionReference constructor; // ignore for now
public:
static void Init(Napi::Env env) {...}
MyNapiObjectWrapper(const CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
// My original code to match the above example
this->someObject = info[0].As<const Napi::External<SomeObject>>().Data();
}
DoSomething()
{
this->someObject->DoSomething();
}
}
I have since come to realise I can pass the address of the shared pointer when creating my external and use it as follows
// modified first sample
{{
// ...
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(info.Env(), &pSomeObject);
auto instance = MyNapiObjectWrapper::Create({ ext });
return instance;
}
// modified second sample
class MyNapiObjectWrapper
{
private:
std::shared_ptr<SomeObject> someObject;
static Napi::FunctionReference constructor; // ignore for now
public:
static void Init(Napi::Env env) {...}
MyNapiObjectWrapper(const CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
// My original code to match the above example
this->someObject =
*info[0].As<const Napi::External<std::shared_ptr<SomeObject>>>().Data();
}
DoSomething()
{
this->someObject->DoSomething();
}
}
So now I am passing a pointer to a shared_ptr to create my Napi::External, my question now though is this OK? Like I said at the start I'm new to c++ but this seems like a bit of a smell. However I tested it with some debugging and could see the reference count go up, so I'm thinking I'm in the clear???
Here the important part of the documentation:
The Napi::External template class implements the ability to create a Napi::Value object with arbitrary C++ data. It is the user's responsibility to manage the memory for the arbitrary C++ data.
So you need to ensure that the object passed by data to Napi::External Napi::External::New exits until the Napi::External<T> object is destructed.
So the code that you have shown is not correct.
What you could do is to pass a Finalize callback to the New function:
static Napi::External Napi::External::New(napi_env env,
T* data,
Finalizer finalizeCallback);
And use a lambda function as Finalize, that lambda could hold a copy through the capture to the shared pointer allowing to keep the shared pointer alive until finalize is called.
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(
info.Env(),
pSomeObject.get(),
[pSomeObject](Env /*env*/, SomeObject* data) {});
I want to make my code more efficient, specifically the reading of data from a text file. Here is a snapshot of what it looks like now:
values V(name);
V.population = read_value(find_line_number(name, find_in_map(pop, mapping)));
V.net_growth = read_value(find_line_number(name, find_in_map(ngr, mapping)));
... // and so on
Basically, the read_value function creates an ifstream object, opens the file, reads one line of data, and closes the file connection. This happens many times. What I want to do is to open the file once, read every line that is needed into the struct, and then close the file connection.
Here is the creating values struct function with parameters:
static values create_struct(std::string name, std::map<std::string, int> mapping) {
values V(name);
V.population = read_value(find_line_number(name, find_in_map(pop, mapping)), file);
V.net_growth = read_value(find_line_number(name, find_in_map(ngr, mapping)), file);
// more values here
return V;
}
The function that calls create_struct is shown below:
void initialize_data(string name) {
// read the appropriate data from file into a struct
value_container = Utility::create_struct(name, this->mapping);
}
I am thinking of instead defining the ifstream object in the function initialize_data. Given what is shown about my program, would that be the best location to create the file object, open the connection, read the values, then close the connection? Also, would I need to pass in the ifstream object into the create_values struct, and if so, by value, reference or pointer?
The short answer is to create your ifstream object first and pass it as reference to your parser. Remember to seek the stream back to the beginning before you leave your function, or when you start to read.
The RAII thing to do would be to create a wrapper object that automatically does this when it goes out of scope.
class ifStreamRef{
ifStreamRef(std::ifstream& _in) : mStream(_in){}
~ifStreamRef(){mStream.seekg(0);}
std::ifstream& mStream;
}
Then you create a wrapper instance when entering a method that will read the fstream.
void read_value(std::ifstream& input, ...){
ifStreamRef autoRewind(input);
}
Or, since the Ctor can do the conversion...
void read_value(ifStreamRef streamRef, ...) {
streamRef.mStream.getLine(...);
}
std::ifstream itself follows RAII, so it will close() the stream for you when your stream goes out of scope.
The long answer is that you should read up on dependency injection. Don't create dependencies inside of objects/functions that can be shared. There are lots of videos and documents on dependency injection and dependency inversion.
Basically, construct the objects that your objects depend on and pass them in as parameters.
The injection now relies on the interface of the objects that you pass in. So if you change your ifStreamRef class to act as an interface:
class ifStreamRef{
ifStreamRef(std::ifstream& _in) : mStream(_in){}
~ifStreamRef(){mStream.seekg(0);}
std::string getLine(){
// todo : mStream.getLine() + return "" on error;
}
bool eof() { return mStream.eof(); }
std::ifstream& mStream;
}
Then later on you can change the internal implementation that would take a reference to vector<string>& instead of ifstream...
class ifStreamRef{
ifStreamRef(std::vector<string>& _in) : mStream(_in), mCursor(0){}
~ifStreamRef(){}
std::string getLine(){
// todo : mStream[mCursor++] + return "" on error;
}
bool eof() { return mCursor >= mStream.size(); }
std::vector<string>& mStream;
size_t mCursor;
}
I have oversimplified a few things.
So, I am trying to log information about the status of the c++ project code in a text file. The program terminates unexpectedly, so I need to append the file as I go rather than storing info in an array along the way. I wanted to call the function to write to the file from within other functions, eventually in the other c++ files as well.
The code is a huge project that has many files and the "main()" technically exists in a separate file from all of the functions that are called throughout the function of the code (therefore not a useful file for me). My plan was to open the file in the setup() function, and then call the function within other functions along the way. Just in case I did not explain the setup of the code well enough, here is the link to the file I am trying to add to: https://github.com/cstracq2/ardupilot/blob/master/ArduCopter/ArduCopter.cpp
I have seen other notes on what may help, but I am not that familiar with c++ and I don't know what most of it means. From what I saw, this is one of the ways I tried, and it is failing to compile.
#include "<existing header>.h"
#include <fstream>
#include <iostream>
void log_data( ofstream &datafile, int value);
void <>::function1()
{ ....<stuff that was already there>
log_data( datafile, <value> );
}
void <>::function2()
{ ....<stuff that was already there>
log_data( datafile, <value> );
}
void setup()
{ ....<stuff that was already there>
ofstream datafile;
datafile.open("data_log_file.txt");
}
void log_data( ofstream &datafile, int value)
{
data_file << value << endl;
}
If there is any advice that you could give me, I would really appreciate it.
In your case I would suggest to use the Singleton Pattern. Here is an example of how you could do it:
class Logger
{
std::ifstream logF;
static Logger *s_instance;
Logger(std::string &path)
{
logF.open(path, std::ios_base::in);
}
public:
void log_data(int val)
{
logF << val << std::endl;
}
static void create_instance(std::string &path)
{
s_instance = new Logger(path);
}
static Logger *instance()
{
return s_instance;
}
};
Now you can just include the header with the class def and call something like:
Logger::instance()->log_data(<value>);
And do not forget to init the class before calling the static method (somewhere in main for instance):
Logger::create_instance(<path>);
Of course, you can just make it easier by hard-coding a value for your path, but if the path changes you'll have to re-compile everything.
Or just use something already implemented like log4cpp
Ah yes now that you mentioned the use of datafile in other function I see the error: The variable datafile is a local variable inside the setup function.
It should either be a member variable or possible a global variable.
Trying to wrap this short example in C++. (and its been a while since I did this).
int main(int argc, char* argv[])
{
//Objects
CFtpConnection* pConnect = NULL; //A pointer to a CFtpConnection object
ftpClient UploadExe; //ftpClient object
pConnect = UploadExe.Connect();
UploadExe.GetFiles(pConnect);
system("PAUSE");
return 0;
}
.h -
class ftpClient
{
public:
ftpClient();
CFtpConnection* Connect();
void GetFiles(CFtpConnection* pConnect);
};
.cpp -
//constructor
ftpClient::ftpClient()
{
}
CFtpConnection* ftpClient::Connect()
{
// create a session object to initialize WININET library
// Default parameters mean the access method in the registry
// (that is, set by the "Internet" icon in the Control Panel)
// will be used.
CInternetSession sess(_T("FTP"));
CFtpConnection* pConnect = NULL;
try
{
// Request a connection to ftp.microsoft.com. Default
// parameters mean that we'll try with username = ANONYMOUS
// and password set to the machine name # domain name
pConnect = sess.GetFtpConnection("localhost", "sysadmin", "ftp", 21, FALSE );
}
catch (CInternetException* pEx)
{
TCHAR sz[1024];
pEx->GetErrorMessage(sz, 1024);
printf("ERROR! %s\n", sz);
pEx->Delete();
}
// if the connection is open, close it MOVE INTO CLOSE FUNCTION
// if (pConnect != NULL)
// {
// pConnect->Close();
// delete pConnect;
// }
return pConnect;
}
void ftpClient::GetFiles(CFtpConnection* pConnect)
{
// use a file find object to enumerate files
CFtpFileFind finder(pConnect);
if (pConnect != NULL)
{
printf("ftpClient::GetFiles - pConnect NOT NULL");
}
// start looping
BOOL bWorking = finder.FindFile("*"); //<---ASSERT ERROR
// while (bWorking)
// {
// bWorking = finder.FindNextFile();
// printf("%s\n", (LPCTSTR) finder.GetFileURL());
// }
}
So basically separated the connection and file manipulation into 2 functions. The findFile() function is throwing the assert. (Stepping into the findFile() and it is specifically at the first ASSERT_VALID(m_pConnection) in inet.cpp. )
How does the way I am passing the arround CFtpConnection* pConnect look?
EDIT - Looks like CObject vfptr is overwritten (0X00000000) in the GetFiles() function.
Any help is appreciated. Thanks.
ANSWER:
This session object must be allocated in the Connection function, with a pointer
declared as a member function of the class. When creating the object within the function,
"CInternetSession sess(_T("MyProgram/1.0"));" the object/session will be terminated when the function exits, being thrown off the stack. When that happens, we can't use the pConnect pointer in other functions.
There is a hierarchy to WinInet objects, with session being the top. If session is gone nothing else can be used. Thus, we must use new to allocate the object in memory so that it sustains after this function exits.
I don't think there is any real value in having the ftpClient class return the CFTPConnection object out of Connect (unless i'm missing something that you intend?) - it should just have that as a Member variable, and GetFiles could use that member directly (Likewise you'd add the CInternetSession as a member of the class and avoid the problem you describe above when it goes out of scope.)
In that manner the ftpClient manages the lifetime of the CFTPConnection and can destroy it in its destructor.