I have a fairly good template (as in snippet of code) I pull out whenever I need a singleton class. I am now trying to apply it within my project to allow me to control a single instance of a web server. I can make a web server without encasing it in my class. When I try to encase it within the class I'm apparently too unskilled to pull it off.
I've tried the obvious Googling and searching here. I've read relevant posts. I am sure this does not mean I have a unique problem, just that I've not figured out the right way to fix it. Here's what I am working with:
webserver.h:
#include <ESP8266WebServer.h>
#include <FS.h>
class WebServer {
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer server();
String getContentType(String);
bool handleFileRead(String);
public:
// Singleton Declarations
static WebServer* getInstance();
~WebServer() {instanceFlag = false;}
// Other Declarations
void initialize(int);
void handleLoop();
};
webserver.cpp:
#include "webserver.h"
bool WebServer::instanceFlag = false;
WebServer* WebServer::single = NULL;
WebServer* WebServer::getInstance() {
if(!instanceFlag) {
single = new WebServer();
instanceFlag = true;
return single;
} else {
return single;
}
}
void WebServer::initialize (int port) {
ESP8266WebServer server(port);
FS *filesystem;
filesystem->begin();
Serial.print("Open: http://");
Serial.print(WiFi.hostname().c_str());
Serial.println(".local");
server.onNotFound([]() {
if (!single->handleFileRead(single->server.uri())) {
single->server.send(404, "text/plain", "404: File not found.");
}
});
server.begin();
Serial.print("HTTP server started on port ");
Serial.print(port);
Serial.println(".");
}
String WebServer::getContentType(String filename) {
if (single->server.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
} else {
return "text/plain";
}
}
bool WebServer::handleFileRead(String path) {
Serial.println("handleFileRead: " + path);
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (filesystem->exists(pathWithGz) || filesystem->exists(path)) {
if (filesystem->exists(pathWithGz)) {
path += ".gz";
}
File file = filesystem->open(path, "r");
single->server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void WebServer::handleLoop() {
single->server.handleClient();
}
The errors I am getting are all similar to the following:
src\webserver.cpp: In member function 'bool WebServer::handleFileRead(String)':
src\webserver.cpp:81:23: error: 'WebServer::single->WebServer::server' does not have class type
single->server.streamFile(file, contentType);
I get the idea of "does not have a class type", I just have no idea what it means here. In my mind, "single" is a pointer to the class so I'm unclear what that reference is not working.
Obviously, there are ample examples out there how to do a web server without encapsulating it. Other things I need to do for this project lend itself to creating that requirement.
There are some mistake in your code.
In webserver.h:
...
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer *server; // <--- remove the parentheses and make it a pointer
String getContentType(String);
bool handleFileRead(String);
...
In webserver.cpp:
In WebServer::initialize I am guessing you want to initialize the class server and filesystem not locals, so it should probably look like this:
void WebServer::initialize (int port) {
server = new ESP8266WebServer(port);
filesystem = new FS();
...
}
And now everywhere you use the server you have to use the -> operator.
For example:
void WebServer::handleLoop() {
single->server->handleClient();
}
Please keep in mind that server and filesystem objects have to be deleted to avoid memory leaks.
EDIT:
You get the new error because FS has no constructor without arguments.
FS's constructor looks like this: FS(FSImplPtr impl) : _impl(impl) { }, here you can see that FSImplPtr is a typedef for std::shared_ptr<FileImpl>, so you need to provide this as a parameter.
It works your way, because SPIFFS's existence is declared here and is of type FS.
If you want to use SPIFFS, you have to use it like this: filesystem = &SPIFFS;, not like you mentioned in the comments (FS* filesystem = &SPIFFS;) because your way creates a new temporary variable named filesystem, and probably you expect to initiate the filesystem in the class, not a local one.
Related
I'm attempting to refactor my code using clang-tidy and it flags a number of changes that should not be applied. For example, I've defined for my cpp files the format:
{ key: readability-identifier-naming.FunctionCase, value: camelBack }
But it flags fixes for functions outside my control like
static void from_json(const nlohmann::json& aJson,...)
Everything I'm working with is contained within a namespace, so is it possible to only apply readability-identifier-naming to content within a namespace? Can this be done with existing functions or will I need to define my own format checker in the clang-tools-extra?
Update
I solved the issue by modifying IdentifierNamingCheck::getDeclFailureInfo and adding a namespace filter to the FileStyle. I pulled some code from another place. It's not a general solution but it works in my case.
// ...
if (!FileStyle.namespaceFilter().empty()) {
auto testNamespace = [&FileStyle](const DeclContext *DC) {
if (!DC->isNamespace())
return false;
const auto *ND = cast<NamespaceDecl>(DC);
if (ND->isInline()) {
return false;
}
if (!DC->getParent()->getRedeclContext()->isTranslationUnit())
return false;
const IdentifierInfo *II = ND->getIdentifier();
return II && II->isStr(FileStyle.namespaceFilter());
};
bool isNamespace = false;
for (const DeclContext *DC = Decl->getDeclContext(); DC;
DC = DC->getParent()) {
isNamespace |= testNamespace(DC);
if (isNamespace)
break;
}
if (!isNamespace)
return llvm::None;
}
// ...
How can I conditionality load and use a shared library if it is installed, but still run without that functionality if it is not there? more specifically, can I do that without using that library as a plugin? I prefer failing during build time rather than runtime if possible.
if I build it with the library flag -lfoo, it builds. But then it fails to run it libfoo.so.2 is not installed in the target system. But if I don't add the library flag it fails in linking.
here is some code snipets for better picture.
myAdapter.cpp
#include "newlib/foo.h" //this is from the shared library
...
bool myAdapter::isAvailable()
{
handle_ = dlopen("libfoo.so.2", RTLD_LAZY);
if (!handle_)
{
return false;
}
return true;
}
...
bool myAdapter::init()
{
if (!isAvailable())
{
return false;
}
isInitilized = false;
isConnected = false;
if (!fooInit()) // shared library function
{
fooCleanup(); //shared library function
return false;
}
// these are my private functions but they call shared library functions.
if (!createUserParams_() || !setCallbacks_() || !createContext_() || !connect_())
{
return false;
}
return true;
}
...
myApp.cpp
#include "myAdapter.h"
...
int main()
{
...
foo = new myAdapter();
if (!foo.init())
{
cout << "Foo function is not available;
isFooAvailable = false;
}
}
...
}
First, remove any linkage you have to libfoo.a, libfoo.sa, or any -lfoo parameter in your build. Everything is to be dynamically loaded.
Second, after dlopen succeeds, you need to get the address, via dlsym, of all the functions you need to use - and call through into those.
Third, instead of making a direct call to your functions, you call through to them via the pointer you loaded via dlsym.
Finally, your isAvailable has a side effect (loading the library) and will re-attempt every time. You just need to load the library once.
Here's an improved version of your code. I made some hypothetical assumptions of what your params to those library functions would be. Change it to meet your needs:
class myAdapter
{
void* handle_;
typedef bool (*CREATE_USER_PARAMS)(UserParams*);
typedef int (*CONNECT)(int);
CREATE_USER_PARAMS createUserParams_;
CONNECT connect_;
myAdapter() :
handle_(),
createUserParams_(),
connect_()
{
handle_ = dlopen("libfoo.so.2", RTLD_NOW);
if (handle_)
{
createUserParams = (CREATE_USER_PARAMS)dlsym(handle_, "createUserParams");
connect_ = (CONNECT)dlsym(handle_, "connect");
if (!createUserParams_ || !connect_)
{
// error
dlclose(handle_);
handle_ = nullptr;
connect = nullptr_;
createUserParams_ = nullptr;
}
}
}
~myAdapter()
{
if (handle_)
{
dlclose(handle_);
handle = nullptr;
}
}
bool createUserParams(UserParams* params)
{
if (createUserParams_ != nullptr)
{
return createUserParams_(params);
}
else
{
return false;
}
}
int connect(int sock)
{
if (connect_ != nullptr)
{
return (connect_(sock));
}
else
{
return -1;
}
}
};
In this example I faced the problem of copying the code:
void BadExample1() {
if (!Initialize1())
return;
if (!Initialize2()) {
Finalize1();
return;
}
if (!Initialize3()) {
Finalize1();
Finalize2();
return;
}
if (!Initialize4()) {
Finalize1();
Finalize2();
Finalize3();
return;
}
// some code..
Finalize1();
Finalize2();
Finalize3();
Finalize4();
}
Bnd here is a bad code structure. If I have a lot of constructs, the width of the code will be too large, this is also bad:
void BadExample2() {
if (Initialize1()) {
if (Initialize2()) {
if (Initialize3()) {
if (Initialize4()) {
if (Initialize5()) {
// some code..
Finalize5();
}
Finalize4();
}
Finalize3();
}
Finalize2();
}
Finalize1();
}
}
How can I save good code sturcture and solve code copying?
Finalize1/2/3 is a API functions and not my program classes.
Maybe some STL containers can solve it?
Maybe something like that?
void GoodExample() {
if (!Initialize1())
return;
RaiiWrapper<void(*)()> raii_wrapper1([]() {
Finalize1();
});
if (!Initialize2()) {
//Finalize1();
return;
}
RaiiWrapper<void(*)()> raii_wrapper2([]() {
Finalize2();
});
if (!Initialize3()) {
//Finalize1();
//Finalize2();
return;
}
RaiiWrapper<void(*)()> raii_wrapper3([]() {
Finalize3();
});
if (!Initialize4()) {
//Finalize1();
//Finalize2();
//Finalize3();
return;
}
RaiiWrapper<void(*)()> raii_wrapper4([]() {
Finalize4();
});
// some code..
//Finalize1();
//Finalize2();
//Finalize3();
//Finalize4();
}
Why not use real objects?
struct SetupPart1 {
SetupPart1 () { if (!Initialize1() throw std::runtime_error("Part1"); }
~SetupPart1 () { Finalize1(); }
};
and so on for part 2, 3, 4, etc.
Now your example looks like this:
void GoodExample() {
try {
SetupPart1 p1;
SetupPart2 p2;
SetupPart3 p3;
SetupPart4 p4;
// some code ...
}
catch { const std::runtime_error &ex ) {
std::cerr << "GoodExample Failed: " << ex.what << std::end;
}
}
You could streamline Marshall's suggestion and use the not-yet-standardized std::make_unique_resource() (this function is closely related to scope_guard, a contraption suggested by Andrei Alexandrescu some years ago and also in that proposal). That gives you an object with two functions - one to run at the variable scope's start, another to run at its end (i.e. on construction and destruction respectively).
Then, instead of defining four separate classes, you'd just write:
void GoodExample() {
auto r1 = std::make_unique_resource(Initialize1, Finalize1);
auto r2 = std::make_unique_resource(Initialize2, Finalize2);
auto r3 = std::make_unique_resource(Initialize3, Finalize3);
auto r4 = std::make_unique_resource(Initialize4, Finalize4);
// some code
}
The proposal has code for the implementation; and - it's not complex at all. So you could just copy the implementation and create your own not_std::make_unique_resource() function and related templated class(es).
Whenever you get a precious resource from an API, you need to wrap it as an object with the appropriate destructor. So, if Initialize1 initialises something1 then something1 should really be an object Something1 that knows how to initialise and how to finalise itself. Also, a failure to initialise should throw an exception (this is not done with fstream because fstream is older than this concept).
class Something1 {
public: Something1 () { if (!Initialize1()) throw resource_failed ("1"); }
~Something1 () { Finalize1(); }
}
I'm attempting to code a state machine based on a gumball machine. I have a interface class of a basic state, while I have specific states that use this interface. I have four states, no_quarter, has_quarter, sold, and sold_out states. I also have a gumball machine class that handles these states and depending on which state my machine is in, it will go that class and do the needed action. Here is my code that is having the problem, I'll post my functions also.
Gumball_Machine.h
class Gumball_Machine
{
private:
int gumball_count;
State *current_state;
No_Quarter_State *nqs;
Has_Quarter_State *hqs;
Sold_State *ss;
Sold_Out_State *sos;
public:
Gumball_Machine(int inventory)
{
gumball_count = inventory;
nqs = new No_Quarter_State(this);
hqs = new Has_Quarter_State(this);
ss = new Sold_State(this);
sos = new Sold_Out_State(this);
if (gumball_count == 0)
set_state(sos);
else
set_state(nqs);
}
void insert_quarter()
{
current_state->insert_quarter();
}
void eject_quarter()
{
current_state->eject_quarter();
}
void turn_crank()
{
current_state->turn_crank();
}
void dispense()
{
current_state->dispense();
}
void set_state(State *new_state)
{
current_state = new_state;
}
State *get_no_quarter_state()
{
return nqs;
}
State *get_has_quarter_state()
{
return hqs;
}
State *get_sold_state()
{
return ss;
}
State *get_sold_out_state()
{
return sos;
}
No_Quarter_State.h
#ifndef NO_QUARTER_STATE_H_INCLUDED
#define NO_QUARTER_STATE_H_INCLUDED
#include "State.h"
class No_Quarter_State: public State
{
public:
No_Quarter_State(Gumball_Machine *gbm);
void insert_quarter();
void eject_quarter();
void turn_crank();
void dispense();
};
#endif // NO_QUARTER_STATE_H_INCLUDED
No_Quarter_State.cpp
#include "No_Quarter_State.h"
#include "Gumball_Machine.h"
No_Quarter_State::No_Quarter_State(Gumball_Machine *machine)
{
machine = machine;
}
void No_Quarter_State::insert_quarter()
{
cout << "You inserted a quarter.\n";
machine->set_state(machine->get_has_quarter_state());
}
void No_Quarter_State::eject_quarter()
{
cout << "You must insert a quarter before you can eject one.\n";
}
void No_Quarter_State::turn_crank()
{
cout << "You must insert a quarter before you can crank the handle.\n";
}
void No_Quarter_State::dispense()
{
cout << "You need to pay first before you can get a gumball.\n";
}
The line I'm having an issue is in the No_Quarter_State.cpp
machine->set_state(machine->get_has_quarter_state());
This is giving me a run-time error. I've seen examples like this but I'm not completely sure if this is legal in C++. I'm attempting to switch the state of my gumball machine object.
The error I get is a generic not responding error: "test.ext has stopped working". I'm using CodeBlocks to code this.
In the constructor, the presumed member variable machine is hidden by the parameter.
No_Quarter_State::No_Quarter_State(Gumball_Machine *machine)
{
machine = machine;
}
You can fix this by using initializer list syntax instead:Thanks Sneftel and NathanOliver
No_Quarter_State::No_Quarter_State(Gumball_Machine *machine)
: machine(machine)
{
}
However, in regular method functions, you would have to use this-> if you named the method parameter the same as the member variable. A typical style used to avoid that issue is to prepend m_ or append _ to member names.
Ive been staring at my code and I can't figure out why on earth my constructor is not gettign called.
It's just ignoring my constructor completely (i've check with stepping with debugger).
Here's my testapp:
using namespace MyEngine;
int _tmain(int argc, _TCHAR* argv[])
{
TestManager* testMgr = new TestManager();
testMgr->RunAllTests();
delete testMgr;
getchar();
return 0;
}
TestManager.h:
namespace MyEngine
{
class TestManager
{
public:
TestManager();
TestManager(uint64_t RepeatTimes);
~TestManager();
bool RunAllTests();
bool RunMemoryTests();
private:
Engine* mEngine;
ILogManager* mLogger;
MemoryTestManager* mMemTestMgr;
uint64_t mRepeatTimes;
};
}
and TestManager.cpp
namespace MyEngine
{
TestManager::TestManager()
{
TestManager(1);
}
TestManager::TestManager(uint64_t RepeatTimes)
{
if (RepeatTimes>0)
mRepeatTimes = RepeatTimes;
else
{
mRepeatTimes = 1;
}
mEngine = Engine::GetEngine();
mMemTestMgr = new MemoryTestManager();
}
TestManager::~TestManager()
{
}
/* Runs all testing modules */
bool TestManager::RunAllTests()
{
bool res = true;
/* Init Engine */
if(mEngine->Init(0,0,0))
{
res = true;
res && mEngine->GetRenderManager()->Init();
res && mLogger->Init(true,true);
res && mEngine->GetMemoryManager()->Init(false);
}
else
return false;
/* Start Engine */
mEngine->Start();
/* Get logger */
mLogger = mEngine->GetLogger();
/* Run Memory Tests */
res &= RunMemoryTests();
if (res)
mLogger->LogInfo("TEST: TESTING SUCCESSFULL");
else
mLogger->LogError("TEST: TESTING FAILED");
return res;
}
/* Runs all memory tests */
bool TestManager::RunMemoryTests()
{
bool res = true;
res &= mMemTestMgr->AllocateTest();
res &= mMemTestMgr->ReferenceTest();
if (res)
mLogger->LogInfo("TEST: RunMemoryTests SUCCESSFULL");
else
mLogger->LogError("TEST: RunMemoryTests FAILED");
return res;
}
}
You cant call another constructor from the same class. I'd refactor the init code into a separate method and call it from both constructors:
namespace MyEngine
{
TestManager::TestManager()
{
Init(1);
}
TestManager::TestManager(uint64_t RepeatTimes)
{
Init(RepeatTimes);
}
void TestManager::Init(uint64_t RepeatTimes)
{
if (RepeatTimes>0)
mRepeatTimes = RepeatTimes;
else
{
mRepeatTimes = 1;
}
mEngine = Engine::GetEngine();
mMemTestMgr = new MemoryTestManager();
}
}
When you call TestManager(1); inside your TestManager::TestManager() constructor, you're creating another instance of TestManager, using the constructor TestManager::TestManager(uint64_t).
You can't do this on C++, you have to create either a init method, were you set the instance variables to whatever you want, or use optional parameters:
TestManager(uint64_t RepeatTimes = 0);
Then, if you create an instance of TestManager without arguments, you will be calling the TestManager::TestManager(uint64_t)constructor with 0 as the uint64_t argument.
you can't call a default constructor from a overloaded constructor. Why don't you simply create you object like this:
TestManager* testMgr = new TestManager(1);
Using a default argument (as per fontanini's answer) will do what you want in this case.
But if this is a simplified example and you really do want to delegate to another constructor, then that's not possible in C++03 - the line TestManager(1) just constructs a temporary object which goes unused (and the line will probably be optimized out unless the TestManager(uint64_t) constructor has side effects).
However, you can do what you're aiming for here with a C++11 compiler and the following syntax:
TestManager::TestManager() :
TestManager(1)
{
}