Overview
I am trying to develop a C++ application which allows for user-created plugins.
I found a nice library called Pluma (http://pluma-framework.sourceforge.net/) which functionally seems to be exactly what I want.
After going through their tutorial, I was able to (with a bit of difficulty) convince the plugin to compile. However, it refuses to play nice and connect with the main program; returning various errors depending on how I try to implement them.
Problem
If I comment out the line labeled 'Main problem line' (in the last file, main.cpp), the plugin compiles successfully, and the main app can recognize it, but it says that "Nothing registered by plugin 'libRNCypher'", and none of the functions can be called.
If I compile that line, the main application instead says "Failed to load library 'Plugins/libRNCypher.so'. OS returned error: 'Plugins/libRNCypher.so: undefined symbol: _ZTIN5pluma8ProviderE".
My guess is that it has something to do with the way the plugin was compiled, as compiling it initially did not work and Code::Blocks told me to compile with "-fPIC" as a flag (doing so made it compile).
Code
Code below:
Main.cpp
#include "Pluma/Pluma.hpp"
#include "CryptoBase.h"
int main()
{
pluma::Pluma manager;
manager.acceptProviderType< CryptoBaseProvider >();
manager.loadFromFolder("Plugins", true);
std::vector<CryptoBaseProvider*> providers;
manager.getProviders(providers);
return 0;
}
CryptoBase.h
#ifndef CRYPTOBASE_H_INCLUDED
#define CRYPTOBASE_H_INCLUDED
#include "Pluma/Pluma.hpp"
#include <string>
#include <vector>
#include <bitset>
//Base class from which all crypto plug-ins will derive
class CryptoBase
{
public:
CryptoBase();
~CryptoBase();
virtual std::string GetCypherName() const = 0;
virtual std::vector<std::string> GetCryptoRecApps() const = 0;
virtual void HandleData(std::vector< std::bitset<8> > _data) const = 0;
};
PLUMA_PROVIDER_HEADER(CryptoBase)
#endif // CRYPTOBASE_H_INCLUDED
RNCypher.h (This is part of the plugin)
#ifndef RNCYPHER_H_INCLUDED
#define RNCYPHER_H_INCLUDED
#include <string>
#include <vector>
#include <bitset>
#include "../Encoder/Pluma/Pluma.hpp"
#include "../Encoder/CryptoBase.h"
class RNCypher : public CryptoBase
{
public:
std::string GetCypherName() const
{
return "RNCypher";
}
std::vector<std::string> GetCryptoRecApps() const
{
std::vector<std::string> vec;
vec.push_back("Storage");
return vec;
}
void HandleData(std::vector< std::bitset<8> > _data) const
{
char letter = 'v';
_data.clear();
_data.push_back(std::bitset<8>(letter));
return;
}
};
PLUMA_INHERIT_PROVIDER(RNCypher, CryptoBase);
#endif // RNCYPHER_H_INCLUDED
main.cpp (This is part of the plugin)
#include "../Encoder/Pluma/Connector.hpp"
#include "RNCypher.h"
PLUMA_CONNECTOR
bool connect(pluma::Host& host)
{
host.add( new RNCypherProvider() ); //<- Main problem line
return true;
}
Additional Details
I'm compiling on Ubuntu 16.04, using Code::Blocks 16.01.
The second error message seems to not come from Pluma itself, but a file I also had to link, #include <dlfcn.h> (which might be a Linux file?).
I would prefer to use an existing library rather than write my own code as I would like this to be cross-platform. I am, however, open to any suggestions.
Sorry for all of the code, but I believe this is enough to reproduce the error that I am having.
Thank You
Thank you for taking the time to read this, and thank you in advance for your help!
All the best, and happy holidays!
I was not able to reproduce your problem, however looking at
http://pluma-framework.sourceforge.net/documentation/index.htm,
I've noticed that:
in your RNCypher.h file you miss something like
PLUMA_INHERIT_PROVIDER(RNCypher, CryptoBase)
it seems also that there's no file CryptoBase.cpp containing something like
#include "CryptoBase.h"
PLUMA_PROVIDER_SOURCE(CryptoBase, 1, 1);
finally, in CryptoBase.h I would declare a virtual destructor (see Why should I declare a virtual destructor for an abstract class in C++?) and provide a definition to it, while you should not declare a default constructor without providing a definition to it (see for instance Is it correct to use declaration only for empty private constructors in C++?); of course the last consideration is valid unless there's another file in which you have provided such definitions.
Related
So i just learned how to seperate classes and the youtube totourial is stressing on doing this alot, here's the link
https://www.youtube.com/watch?v=NTip15BHVZc&list=PLAE85DE8440AA6B83&index=15
My code is the exact same as his, and in the cpp file theres this thing:
mainClass::myfunction; (mainclass is the name of my class, myfunction is my function)
when i try to execute my program, it gives an error:
unidentified reference to 'mainClass::myfunction()'
here's my main.cpp file code:
#include <iostream>
#include "mainclass.h"
using namespace std;
int main()
{
mainClass bo;
bo.myfunction();
return 0;
}
here's my mainclass.h code:
#ifndef MAINCLASS_H
#define MAINCLASS_H
class mainClass
{
public:
myfunction();
};
#endif // MAINCLASS_H
my mainclass.cpp:
#include "mainclass.h"
#include <iostream>
using namespace std;
mainClass::myfunction()
{
cout << "I am a banana" << endl;
}
I don't know much about these so could you just tell me what the errors here are, because i copied everything correctly from the guy's totourial but still it doesn't work
P.S: this happens to me alot, i understand everything, nothing works, i copy everything, nothing works, and then i literally do exactly what the person is doing, still nothing works on all three of PC's, so i dont think the problem is with the devices lol
I doubt you completely copied and pasted that code because I'm fairly sure a teacher shouldn't be teaching having functions without a specified return type, but let's jump into it anyways...
Possibility #1
You meant to create a constructor for the class. In that case, please make sure the constructor function has the same name as the class. Also, you can't call it through .mainClass(), as it is a constructor.
class mainClass
{
public:
mainClass();
};
mainClass::mainClass()
{
cout << "I am a banana" << endl;
}
Possibility #2 You meant to create the class member function myfunction. You really should be specifying what return type your function is of. Some compilers will auto-assume int return type, and so the function you created is int myfunction();, but you really should be specifying it as void myfunction(); since you didn't return anything. Addl. info: Does C++ allow default return types for functions?
Next, change how you are giving the definition, by adding the return type.
void mainClass::myfunction()
{
cout << "I am a banana" << endl;
}
Possibility #3 Those should work, but another issue is that you might not have linked mainclass.cpp, so there is no definition available. In code blocks, right click on the project name and hit Add Files, then add the mainclass.cpp so the linker can define mainClass::myfunction().
To troubleshoot if the mainclass.cpp is being built with the project, try adding
#error I'm included! to the file mainclass.cpp after #include "mainclass.h". If you get an error I'm included!, then it is linked and you can remove the #error.
I've created a shared library project that does various things with a couple of different classes that I wrote. So my question is more clear, please note the header file below:
TsmClient.h
#include "Headers/Framework/BoostSocket.h"
#include "DataContracts.h"
#ifndef TSMCLIENT_H_
#define TSMCLIENT_H_
using namespace boost;
using namespace boost::asio;
class TsmClient {
public:
TsmClient();
TsmClient(std::string host);
virtual ~TsmClient();
bool IsConnected();
friend class WarehouseFramework;
private:
std::string Communicate(std::string &req);
std::string serverHost;
int port;
std::shared_ptr<BoostSocket> socket;
io_service service;
void Connect();
void Close();
};
By itself, this project builds just fine and produces a .so file. However, when I attempt to use this library in another project, I am having issues accessing a class that I have written in DataContracts.h. The exact error is:
WarehouseSettingTemplateList’ was not declared in this scope
I am able to create an instance of TsmClient and call functions successfully all day long, and since TsmClient.h includes DataContracts.h, I'm a little confused as to why I can't use a class that is declared in DataContracts.h. Does this have to do with the fact that I'm compiling this project as a shared library?
Here is DataContracts.h
#include "Headers/Framework/Proto/TcpFramework.pb.h"
#include "Headers/Framework/Proto/ProductManagement.pb.h"
#include "Headers/Framework/Proto/KioskManagement.pb.h"
#include "Headers/Framework/Proto/WarehouseManagement.pb.h"
#include "Headers/Framework/Proto/CashTillManagement.pb.h"
#include "Headers/Framework/Proto/AccountManagement.pb.h"
#include "Headers/Framework/Proto/VendorManagement.pb.h"
#include "Headers/Framework/Proto/ShoppingCartManagement.pb.h"
#include "Headers/Framework/Proto/Generic.pb.h"
#include "Headers/Framework/Proto/OfflineManagement.pb.h"
#include "Headers/Framework/Proto/CouponManagement.pb.h"
#include "Headers/Utils/Utilities.h"
using namespace StokedProtoBuf;
#ifndef DATACONTRACTS_H_
#define DATACONTRACTS_H_
class WarehouseSettingTemplate {
public:
long idWarehouse;
int idWarehouseSettingType;
std::string Value;
bool IsEnabled;
std::string Serialize();
void Deserialize(const std::string &data);
private:
WarehouseSettingTemplate_Proto proto;
};
class WarehouseSettingTemplateList {
public:
std::vector<WarehouseSettingTemplate> settingList;
std::string Serialize();
void Deserialize(const std::string &data);
private:
WarehouseSettingTemplateList_Proto proto;
};
I'm sure this is a stupid oversight on my part but it is driving me insane.
Hate to answer my own question after all of the help that I've received, but here is what was wrong...
I had a header file in my shared library called DataContracts.h. In my main project, I also had a header file called DataContracts.h
So I think that my include guards where preventing the shared library header file to be imported. This is the first time that I've really worked with shared libraries before so I hope that this will help other people if they run into this issue.
--I feel bad because I did not provide enough information for anybody else to answer this question.
First its better that you have your header guard (#ifndef/#define) on the top of your file (above your includes)
Second, make sure that every class is available in your .so and the program you linking against.
Third, You should not use inline declarations in your classes which will be exported otherwise at compile time the linker cannot choose which definition to use (the definition in your .so file or your program).
I am trying to get to grips with inheritance in C++ before trying to implement something in a larger file. I realise this question has been asked before but I've scoured literally everything I could find on this - nothing pointed me towards a fix. So hopefully a kind SO member can help me.
I writing a library for Arduino just to be clear. Here is my code:
CtrlBrd.h
#ifndef CtrlBrd_h
#define CtrlBrd_h
#include "Arduino.h"
class CtrlBrdClass
{
public:
CtrlBrdClass();
};
extern CtrlBrdClass CtrlBrd;
#endif
CtrlBrd.cpp
#include "Arduino.h"
#include "CtrlBrd.h"
CtrlBrdClass::CtrlBrdClass() {
}
int CtrlBrdClass::test()
{
return 79;
}
CtrlBrdClass CtrlBrd;
CtrlBrdEx.h
#ifndef CtrlBrdEx_h
#define CtrlBrdEx_h
#include <CtrlBrd.h>
class CtrlBrdEx : public CtrlBrdClass { // <----- Getting the error here!!
public:
CtrlBrdEx();
int test2();
};
extern CtrlBrdEx CtrlBrd;
#endif
CtrlBrdEx.cpp
#include "CtrlBrdEx.h"
int CtrlBrdEx::test2() {
return CtrlBrd.test() +1;
}
CtrlBrdEx CtrlBrd;
Error:
error: expected class-name before '{' token
Replace
#include <CtrlBrd.h>
with
#include "CtrlBrd.h"
The exact sequence of locations searched by the compiler is implementation dependent in both cases (§16.2 [cpp.include]), but both gcc and VC (and every other compiler if I had to guess) will search the current directory for the quoted form, but not necessarily for the other.
It seems the only solution is to include both files at the top of your main .ino code file. The Arduino compiler doesn't seem to like including libraries from within libraries...
I have a very weird problem... First of all, here are my class's files:
Show.h:
#ifndef SHOW
#define SHOW
#include <iostream>
#include <string>
#include <string.h>
class Show
{
private:
std::string m_time;
int m_serial
public:
Show(const std::string &time, const int &serial);
~Show();
};
#endif
Show.c:
#include "Show.h"
Show::Show(const std::string &time,const int &serial)
{
}
As you can probably see, I only wrote the declaration of the constructor, when the visual studio underlined the second "Show" word in the Show.c file, and told me:
"void Show::Show(const std::string &time,const int &serial)
Show::Show()
Show::Show(const Show &)
Error: no instance of overloaded function "Show::Show" matches the specific type"
And when I stand with the mouse cursor over the constructor function in the Show.h file it looks like that function doesn't exists... I have written some classes in c++ before, and that is the first time anything like that happens to me... help please :(
You forgot to put ; after m_serial field variable. I believe this is the reason of the problem you have. Unfortunately many compilers do not give the exact reason of the problems like this, so you have to be careful with syntax rules.
I'm racking my brain trying to find out how to write cross platform classes while avoiding the cost of virtual functions and any kind of ugliness in the platform specific versions of classes. Here is what I have tried.
PlatformIndependantClass.hpp
class PlatformIndependantClass {
public:
PlatformIndependantClass();
std::string GetPlatformName();
private:
PlatformIndependantClass* mImplementation;
};
LinuxClass.hpp
#include "PlatformIndependantClass.hpp"
class LinuxClass : public PlatformIndependantClass{
public:
std::string GetPlatformName();
};
WindowsClass.hpp
#include "PlatformIndependantClass.hpp"
class WindowsClass : public PlatformIndependantClass {
public:
std::string GetPlatformName();
};
PlatformIndependantClass.cpp
#include "PlatformIndependantClass.hpp"
#include "LinuxClass.hpp"
#include "WindowsClass.hpp"
PlatformIndependantClass::PlatformIndependantClass() {
#ifdef TARGET_LINUX
mImplementation = new LinuxClass();
#endif
#ifdef TARGET_WINDOWS
mImplementation = new WindowsClass();
#endif
}
std::string PlatformIndependantClass::GetPlatformName() {
return mImplementation->GetPlatformName();
}
LinuxClass.cpp
#include "LinuxClass.hpp"
std::string LinuxClass::GetPlatformName() {
return std::string("This was compiled on linux!");
}
WindowsClass.cpp
#include "WindowsClass.hpp"
std::string WindowsClass::GetPlatformName() {
return std::string("This was compiled on windows!");
}
main.cpp
#include <iostream>
#include "PlatformIndependantClass.hpp"
using namespace std;
int main()
{
PlatformIndependantClass* cl = new PlatformIndependantClass();
cout << "Hello world!" << endl;
cout << "Operating system name is: " << cl->GetPlatformName() << endl;
cout << "Bye!" << endl;
return 0;
}
Now, this compiles fine but I get a segmentation fault. I believe this is because the platform specific classes inherit from PlatformIndependantClass, which on construction, creates an instance of the platform specific class, so I get infinite recursion. Every time I try, I just get extremely confused!
How can I achieve a design like this properly? Or is this just a horrible idea. I have been trying to find out how to write cross platform classes but I just get a load of results about cross platform libraries, any help will be gratefully accepted :)
I think what you are trying to accomplish can be accomplished much easier...
Object.h:
#include <normal includes>
#if WINDOWS
#include <windows includes>
#endif
#if LINUX
#include <linux includes>
#endif
class Object
{
private:
#if WINDOWS
//Windows Specific Fields...
#endif
#if LINUX
//Linux Specific Fields...
#endif
public:
//Function that performs platform specific functionality
void DoPlatformSpecificStuff();
//Nothing platform specific here
void DoStuff();
};
Object.cpp
#include "Object.h"
void Object::DoStuff() { ... }
ObjectWin32.cpp
#if WINDOWS
#include "Object.h"
void Object::DoPlatformSpecificStuff()
{
//Windows specific stuff...
}
#endif
ObjectLinux.cpp
#if LINUX
#include "Object.h"
void Object::DoPlatformSpecificStuff()
{
//Linux specific stuff...
}
#endif
And so on. I think this could accomplish what you are trying in a bit easier fashion. Also, no virtual functions needed.
Starting from the end, yes, truly a horrible idea, as are most ideas that start with "I want to avoid the cost of virtual functions".
As to why you're getting the segmentation fault (stack overflow specifically), it's because you aren't using virtual functions, but static linking. The compiler doesn't know that mImplementation is anything but a PlatformIndependantClass, so when you try to call return mImplementation->GetPlatformName() you're calling the same function over and over.
What you achieved is called shadowing, you're using compile-time function resolution. The compiler will call the GetPlatformName function of the actual type of the variable you're calling it from, since there's no virtual table to overwrite the pointers to the actual functions. Since mImplementation is PlatformIndependantClass, mImplementation->GetPlatformName will always be PlatformIndependantClass::GetPlatformName.
Edit: Of course the question of why you need to create both a Windows and a Linux copy of your engine at the same time comes to mind. You'll never use both of them at the same time, right?
So why not just have two different libraries, one for each system, and link the right one from your makefile. You get the best of all worlds!
Instead of using the constructor to build the platform-specific instance, I would create a static factory method to create the instances:
PlatformIndependantClass* PlatformIndependantClass::getPlatformIndependantClass() {
#ifdef TARGET_LINUX
return new LinuxClass();
#endif
#ifdef TARGET_WINDOWS
return new WindowsClass();
#endif
}
This way you avoid the recursion, and you also don't need your mImplementation pointer.
I would also try to avoid platform-specific classes, but that's another story :)
When you want to have polymorphic behavior without any run-time overhead, you can try the curiously recurring template pattern (CRTP). The base class is a template, and the derived class uses itself as the template parameter for the base. This requires your classes to be defined as templates, which further restricts them to be implemented completely in the header (.hpp) files.
I'm not sure how to apply the pattern in your particular case.
I don't think the constructor is causing the infinite recursion. It's the GetPlatformName() function. Because it's not set as virtual, it can only call itself.
Two solutions: Make that function virtual, or do away with the inheritance completely.
Either way, the cost of a function only calling another function will be more expensive than using virtual functions in the first place. So I would say keep the inheritance, and virtualize the functions specific to the platform, and call them directly, without going through a base class function.
You are correct about the infinte loop. The fix is actually easier than you'd think.
PlatformIndependantClass.hpp
#include //portable headers
struct PlatformDependantClass; //defined in Cpp file
class PlatformIndependantClass {
public:
PlatformIndependantClass();
~PlatformIndependantClass();
std::string GetPlatformName();
private:
std::unique_ptr<PlatformDependantClass> mImplementation; //note, different type
};
LinuxClass.cpp
#ifdef __GNUC__
#include //linux headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass { //linux only stuff
//stuff
};
PlatformIndependantClass() {
mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
return std::string("This was compiled on linux!");
}
#endif //__GNUC__
WindowsClass.cpp
#ifdef _MSC_VER
#include //windows headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass { //windows only stuff
//stuff
};
PlatformIndependantClass() {
mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
return std::string("This was compiled on Windows!");
}
#endif //_MSC_VER
There's only ONE class defined here. In windows, it only compiles and contains windows stuff, and in Linux, it only compiles and contains linux stuff. Note that the void* thing is called an "Opaque pointer" or "pimpl idiom" http://en.wikipedia.org/wiki/Opaque_pointer