I'm using the rpcgen libraries to create an application where I have to use a hashmap on the server side. Is it advisable to use the STL libraries (or any C++ code) with rpcgen? I've tried compiling the files with g++ and it works. Or would I be better off implementing something like a linked list instead of a hashmap (I'm assuming complexity is not an issue) while sticking with C?
Something like this : My input file is
struct intpair {
int a;
int b;
};
program ADD_PROG {
version ADD_VERS {
int ADD(intpair) = 1;
} = 1;
} = 0x23451111;
(from http://www.cs.rutgers.edu/~pxk/rutgers/notes/rpc/index.html).
I want to use a hashmap on the server side. I tried doing something like this in my server side file:
#include "add.h"
#include <map>
#include <string>
int *
add_1_svc(intpair *argp, struct svc_req *rqstp)
{
std::map<std::string, int> voteList;
static int result;
std::string s = "Aa";
voteList.insert(std::pair<std::string, int> ("ABC", 100));
printf("Add called\n");
return &result;
}
and it works. I did have to rename the files and use g++ though.
It looks like the C++ STL components don't "leak" through the interface you're implementing, so it should be all fine and good. One thing to be aware of is exception safety: you might want to add a top-level try/catch block to convert any exceptions into an appropriate error.
Related
I've been trying to come up with a means of generating a C interface for a C++17 project of mine. The project produces an executable that loads plugins on the fly. I played with clang for a while before discovering SWIG, and I'm wondering if SWIG is up to the task, or if there's a trivial amount of work that I can do to make it suitable for this scenario.
Here's my vision of the plugin interface. Suppose the source code of my program looks like this:
header.h
namespace Test {
struct TestStruct {
int Data;
};
class TestClass {
public:
virtual ~TestClass() = default;
void TestMethod(TestStruct&) const;
virtual void TestVirtual(int);
};
}
then the following code should be generated:
api.h
// opaque structs
typedef struct {} Test_TestStruct;
typedef struct {} Test_TestClass;
typedef struct {
void (*Test_TestClass_destructor)(Test_TestClass*);
void (*Test_TestClass_TestVirtual)(Test_TestClass*, int);
} Test_TestClass_vtable;
typedef struct {
Test_TestStruct *(*Test_TestStruct_construct)();
void (*Test_TestStruct_dispose)(Test_TestStruct*);
int *(*Test_TestStruct_get_Data)(Test_TestStruct*);
int *(*Test_TestStruct_set_Data)(Test_TestStruct*, int);
Test_TestClass *(*Test_TestClass_construct)();
Test_TestClass *(*Test_TestClass_construct_derived(const Test_TestClass_vtable*);
void (*Test_TestClass_dispose)(Test_TestClass*);
void (*Test_TestClass_TestMethod)(const Test_TestClass*, Test_TestStruct*);
void (*Test_TestClass_TestVirtual)(Test_TestClass*, int);
} api_interface;
api_host.h
#include "api.h"
void init_api_interface(api_interface&);
api_host.cpp
#include "header.h"
#include "api.h"
// wrapper class
class _derived_TestClass : public Test::TestClass {
public:
_derived_TestClass(const Test_TestClass_vtable &vtable) : _vtable(vtable) {
}
~_derived_TestClass() {
if (_vtable.Test_TestClass_destructor) {
_vtable.Test_TestClass_destructor(reinterpret_cast<Test_TestClass*>(this));
}
}
void TestVirtual(int v) override {
if (_vtable.Test_TestClass_TestVirtual) {
_vtable.Test_TestClass_TestVirtual(reinterpret_cast<Test_TestClass*>(this), v);
} else {
TestClass::TestVirtual(v);
}
}
private:
const Test_TestClass_vtable &_vtable;
};
// wrapper functions
Test_TestStruct *_api_Test_TestStruct_construct() {
return reinterpret_cast<Test_TestStruct*>(new TestStruct());
}
void _api_Test_TestStruct_dispose(Test_TestStruct *p) {
auto *phost = reinterpret_cast<TestStruct*>(p);
delete phost;
}
int *_api_Test_TestStruct_get_Data(Test_TestStruct *p) {
return &reinterpret_cast<TestStruct*>(p)->Data;
}
...
...
// sets the values of all function pointers
void init_api_interface(api_interface &iface) {
iface.Test_TestStruct_construct = _api_Test_TestStruct_construct;
iface.Test_TestStruct_dispose = _api_Test_TestStruct_dispose;
iface.Test_TestStruct_get_Data = _api_Test_TestStruct_get_Data;
...
...
}
When I compile the host program, I compile all these files into an executable, and call init_api_interface() to initialize the function pointers. When other people compile plugins, they only include api.h, and compile the files into a dynamic library with a certain exposed function, say init_plugin(const api_interface*). When the user loads a plugin, the host program only needs to pass a pointer to the struct to init_plugin in the dynamic library, and the plugin can set off to use all these functions.
The benefits of using such a scheme is that:
Plugins compiled using different toolchains than the host program should work fine.
The list of API functions can be extended without breaking existing plugins, as long as new function pointers are added after existing ones.
This approach allows full access to routines in the host program, while it's also easy to hide certain aspects.
It allows plugins to inherit from classes in the host program, which is kinda important for my case.
Plugin developers don't need the source of the host program.
It's convenient since the API interface doesn't need to be manually maintained.
Of course, this is just a gist of the approach and many more details need to be considered in practice.
So my questions are:
Is this kind of plugin interface good practice? Are there existing examples of this approach? Are there better solutions to this problem? Is there any critical drawbacks of this approach that I don't see?
Can SWIG accomplish this task? If not, can SWIG be modified to do so?
If SWIG must be modified, which is easier, modifying SWIG or starting from scratch using clang?
I am trying to unit test an HTTP API written in C++:
void getLogNames(Request & req, Response & res)
{
vector<string> files = getFilesInDirectory(LOG_LOCATION, ".log", false);
json response(files);
res.send(response);
}
The problem is that LOG_LOCATION is included from common.h and is const, and can't be changed by my testing code:
const std::string LOG_LOCATION = "/var/log"
I've tried doing this at the top of the file:
#ifdef UNIT_TEST
#include <common_mock.h>
#else
#include <common.h>
#endif
However, common.h is included in some shared libraries that are being linked in, and I would have to add UNIT_TEST hooks to all those files and rebuild the shared libraries as well, which I would rather avoid...
Is there an easier way I could be doing this, some #define tricks or something?
Well, you can try to const_cast a pointer to your LOG_LOCATION but it's dirty and unreliable solution and may cause seg fault. For example:
original_file.h
#include <iostream>
const std::string LOG_LOCATION = "/var/log";
int func() {
std::cout << LOG_LOCATION << std::endl;
}
unit_test.cpp
#include "test.h"
void someUnitTest() {
const std::string* cs = &LOG_LOCATION;
std::string* s = const_cast<std::string*>(cs);
*s = "NEW_VALUE";
std::cout << *s;
}
int main() {
someUnitTest();
}
This code may work in some cases (i.e. this successfully compiled and worked in GCC but only for class object type - it crashes with buildin type like int) but is may change with different compilers, platforms, or optimization levels.
The recommended way is to redesign your application and use dependency injections, for example wrap your function calls in a class and put this location as a settable member.
Why don’t you change your class to receive the log location in its constructor? By hardcoding it (macros are eqivalent to hardcoding from the testing point of view) you’re purposely making your class less testable.
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.
This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 7 years ago.
Ok so I'm still getting used to C++ again, so this error may seem simple. I'm attempting to create a simple class with a three member functions (I'm only trying to call one of them in this). So I create the class, instantiate an object, then attempt to call the function using that object and this error comes up:
Code.cpp:(.text+0x15): undefined reference to `Code::genCode()'
I've double checked to see if it was an error with the function itself, but that is not the case. I've seen others post about this issue but there seems to be a multitude of situations and solutions. Anyway here's the code:
#include <vector>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <list>
using namespace std;
class Code {
public:
int genCode();
int checkCorrect();
int checkIncorrect();
};
int main()
{
Code c1;
c1.genCode();
}
////////////////FUNCTIONS/////////////////////////
int genCode()
{
vector <int> newcode;
srand(time(NULL));
for(int i = 0; i < 9; i++){
int x;
x = (rand() % 6);
if (find(newcode.begin(),newcode.end(), x) == newcode.end())
{
newcode.push_back(x);
}
}
if (newcode.size() > 4)
{
newcode.pop_back();
}
for(int i = 0; i < 4; i++)
{
return newcode[i];
}
}
int checkCorrect()
{
}
int checkIncorrect()
{
}
you need to put class name before method name
the format is
'returnType Classname::methodname
{
codes
}'
int code::genCode()
{
//codes
}
or you also possible to write code in class
Change the implementation of the methods of your class to the following:
int Code::genCode()
{
...
}
int Code::checkCorrect()
{
...
}
int Code::checkIncorrect()
{
...
}
You are defining the functions outside the class. Put them inside the class, then you don't need to declare them inside the class. Directly define them.
#include <vector>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <list>
using namespace std;
class Code {
public:
////////////////FUNCTIONS/////////////////////////
int genCode()
{
cout << "Inside genCode. Just for Debugging purpose." << endl;
vector <int> newcode;
srand(time(NULL));
for(int i = 0; i < 9; i++){
int x;
x = (rand() % 6);
if (find(newcode.begin(),newcode.end(), x) == newcode.end())
{
newcode.push_back(x);
}
}
if (newcode.size() > 4)
{
newcode.pop_back();
}
for(int i = 0; i < 4; i++)
{
return newcode[i];
}
}
int checkCorrect()
{
}
int checkIncorrect()
{
}
};
int main()
{
Code c1;
c1.genCode();
}
Otput:
Inside genCode. Just for Debugging purpose.
The function
int genCode()
Is what's called a free function. It is not bound to a class.
In order for the compiler to know that genCode is part of a class, you have to tell it by explicitly stating the namespace to which genCode belongs.
int Code::genCode()
However since it appears code Code is entirely contained within one file, following CodeRunner's advice will lead to a cleaner implementation.
But why would anyone want to got the trouble of splitting everything up?
Separating the class definition from the method implementations allows you place the class definition into one file, the 'h header file, and the methods in an implementation file, usually a .cpp file. The header file is then shared with users of the Code object and the implementation file can be compiled into a library and hidden from the callers view.
There are a number of reasons to do this, but most of them have to do with creating pre-compiled libraries and using them to reduce build times.
With a library, you build the library once, and then compile the rest of the code that uses the library over and over until you get it right. Can you imagine how long it would take to build a program if you had to rebuild the C++ standard library every time you fixed a bug and wanted to test?
Had a job like that once. Had to spend four hours compiling third party network code every time I made a fix because the company's paranoid build system rebuilt everything every time. Off by one error? 4 hours. Need to add a debug line? 4 hours. You could make and test three changes a day. Sure, you can batch up a bunch of fixes, but if one failed spectacularly and broke the system, which one was it? Sooner or later you're reduced to a crawl, making tweaks, building, testing, profiling one at a time. Fortunately I was working on contract and paid by the hour.
Another good example is you can have one library that supports Windows and other libraries supporting QNX and other operating systems. All use the same header and the user can write a program that, in theory, will operate on all supported platforms simply by recompiling the user's code. It's never quite that clean, but one can dream.
The library can even be replaced with an updated library without requiring changes or compilation of the user's code and different variants of the library can exist for different needs. A debug version with extra logging, for example.
Perhaps the implementation is not intended for public eyes. The users get to see the header and call functions in the library, but no more.
Following an example at: http://www.learncpp.com/cpp-tutorial/47-structs/ relating to structs, and when I tried to compile this program:
#include <iostream>
void PrintInformation(Employee sEmployee)
{
std::cout<<"ID: "<<sEmployee.nID<<std::endl;
std::cout<<"Age: "<<sEmployee.nAge<<std::endl;
std::cout<<"Wage: "<<sEmployee.fWage<<std::endl;
}
struct Employee {int nID;int nAge;float fWage;};
int main()
{
Employee abc;
abc.nID=123;
abc.nAge=27;
abc.fWage=400;
// print abc's information
PrintInformation(abc);
return 0;
}
I get the following:
Why is that?
Thanks.
You need to declare the struct before the function that attempts to use it.
C (and by extension, C++) were designed for "single-pass" compilation. Therefore, everything must be available to the compiler by the time it's required.