Using Eclpse on Linux I have defined a C++ class, named ABC (ABC.hpp):
#ifndef ABC_HPP_
#define ABC_HPP_
#include <stdio.h>
// namespace my { <---- COMMENTED OUT
class ABC {
private:
int m_number;
public:
ABC(int p_number);
void doSomething();
virtual ~ABC();
};
// } /* namespace my */ <---- COMMENTED OUT
#endif /* ABC_HPP_ */
and its implementation is (ABC.cpp):
#include "ABC.hpp"
// using namespace my; <---- COMMENTED OUT
ABC::ABC(int p_number) {
this->m_number = p_number;
}
ABC::~ABC() {
this->m_number = -1;
}
void ABC::doSomething() {
printf("doing something (%d)\n", this->m_number);
}
To use this class in C programs, I have created a layer containing these methods (ABCwrapper.h):
typedef void CABC;
#ifdef __cplusplus
extern "C" {
#endif
CABC* create_abc();
void call_abc_methods(const CABC *p_abc);
void destroy_abc(CABC *p_abc);
#ifdef __cplusplus
} // extern "C"
#endif
and
#include "ABC.hpp"
#include "ABCWrapper.h"
extern "C" {
CABC* create_abc() {
ABC* abc = new ABC();
return (ABC*)abc;
}
void call_abc_methods(const CABC *p_abc) {
ABC* abc = (ABC*)p_abc;
abc->doSomething();
}
void destroy_abc(CABC *p_abc) {
ABC* abc = (ABC*)p_abc;
delete abc;
}
}
That's fine and I can use the ABC class instance. But what about the define the ABC class in a name space, let's say "my"? If I remove the comment signs from all name space lines the IDE complains saying that the "Type 'ABC' could not be resolved".
If I want to extend my C++ library I have to use name space for my classes but then I don't know how to use in wrappers. Please, help me to solve this mystery.
Thank you.
SK
Some mostly minor nitpicks about the code. You've already got the details of the solution with an active namespace, but there are various minor issues that should be addressed.
You would be better off introducing an incomplete type, typedef struct CABC CABC; (in place of typedef void CABC;), so that you get some type safety in the C code. This would prevent you passing a random FILE *, for example, to the C interface functions, which using void won't.
When you use the incomplete type, you should use reinterpret_cast<ABC *>(abc) in the C wrapper functions instead of C-style casts. The compiler then shows up a problem with const-ness in the call_abc_methods() function; the argument should not be const (but the C-style cast hid the problem).
Additionally, your ABC.hpp header shows a common (minor) mistake; it includes an extraneous header (#include <stdio.h>) that is not needed to use the header safely. That line should only appear in the implementation file, ABC.cpp, where the code uses the services of <stdio.h>. Most headers should #include only those other headers necessary to make the header usable on its own. They should not include random other headers.
Here's a complete working program — which has a lot of files. There are 3 headers:
ABC.hpp — declaring class ABC.
ABCwrapper.h — declaring the C interface to class ABC.
ABCprogram.h — bilingual header declaring other functions.
There is 1 C file:
ABCuser.c — there must be some C code that needs to use the C interface to class ABC to make the whole exercise worthwhile, and this is it.
There are 3 C++ files:
ABC.cpp — defining class ABC.
ABCwrapper.cpp — defining the C interface to class ABC.
ABCmain.cpp — the main program in a bilingual system should normally be written in C++.
And there's a makefile.
ABC.hpp
#ifndef ABC_HPP_INCLUDED
#define ABC_HPP_INCLUDED
namespace abc_library {
class ABC {
private:
int m_number;
public:
ABC(int p_number);
void doSomething();
virtual ~ABC();
};
} /* namespace abc_library */
#endif /* ABC_HPP_INCLUDED */
ABCwrapper.h
#ifndef ABCWRAPPER_H_INCLUDED
#define ABCWRAPPER_H_INCLUDED
typedef struct CABC CABC; // Pointer to this ncomplete type used in C code
#ifdef __cplusplus
extern "C" {
#endif
CABC *create_abc(int val);
void call_abc_methods(CABC *p_abc);
void destroy_abc(CABC *p_abc);
#ifdef __cplusplus
}
#endif
#endif /* ABCWRAPPER_H_INCLUDED */
ABCprogram.h
#ifndef ABCPROGRAM_H_INCLUDED
#define ABCPROGRAM_H_INCLUDED
#if defined(__cplusplus)
extern "C" {
#endif
extern int c_code_function(int init);
#if defined(__cplusplus)
}
#endif
#endif /* ABCPROGRAM_H_INCLUDED */
ABCuser.c
#include "ABCwrapper.h"
#include "ABCprogram.h"
int c_code_function(int init)
{
CABC *abc = create_abc(init);
call_abc_methods(abc);
destroy_abc(abc);
return 0;
}
ABC.cpp
#include "ABC.hpp"
#include <stdio.h>
using namespace abc_library;
ABC::ABC(int p_number) {
this->m_number = p_number;
}
ABC::~ABC() {
this->m_number = -1;
}
void ABC::doSomething() {
printf("doing something (%d)\n", this->m_number);
}
ABCwrapper.cpp
#include "ABC.hpp"
#include "ABCwrapper.h"
using namespace abc_library;
extern "C" {
CABC *create_abc(int val) {
ABC* abc = new ABC(val);
return reinterpret_cast<CABC*>(abc);
}
void call_abc_methods(CABC *p_abc) {
ABC *abc = reinterpret_cast<ABC *>(p_abc);
abc->doSomething();
}
void destroy_abc(CABC *p_abc) {
ABC* abc = reinterpret_cast<ABC *>(p_abc);
delete abc;
}
}
ABCmain.cpp
#include "ABCprogram.h"
int main()
{
return c_code_function(39);
}
makefile
CC = gcc # /usr/bin/gcc
CXX = g++
RM_FR = rm -fr --
WFLAGS = -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition
SFLAGS = -std=c99
OFLAGS = -g -O3
UFLAGS = # Set on make command line only
OXXFLAGS = -g -O3
SXXFLAGS = -std=c++11
WXXFLAGS = -Wall -Wextra
UXXFLAGS = # Set on make command line only
LDFLAGS =
LDLIBS =
CFLAGS = ${OFLAGS} ${SFLAGS} ${WFLAGS} ${UFLAGS}
CXXFLAGS = ${OXXFLAGS} ${SXXFLAGS} ${WXXFLAGS} ${UXXFLAGS}
PROGRAM = abc
FILES.cpp = \
ABC.cpp \
ABCmain.cpp \
ABCwrapper.cpp
FILES.c = \
ABCuser.c
FILES.h = \
ABCprogram.h \
ABCwrapper.h
FILES.o = ${FILES.cpp:.cpp=.o} ${FILES.c:.c=.o}
all: ${PROGRAM}
${PROGRAM}: ${FILES.o}
${CXX} -o $# ${CXXFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}
clean:
${RM_FR} *.o *.dSYM core a.out
depend:
mkdep ${FILES.cpp} ${FILES.c}
# DO NOT DELETE THIS LINE or the blank line after it -- make depend uses them.
ABC.o: ABC.cpp
ABC.o: ABC.hpp
ABCmain.o: ABCmain.cpp
ABCmain.o: ABCprogram.h
ABCuser.o: ABCprogram.h
ABCuser.o: ABCuser.c
ABCuser.o: ABCwrapper.h
ABCwrapper.o: ABC.hpp
ABCwrapper.o: ABCwrapper.cpp
ABCwrapper.o: ABCwrapper.h
In ABCWrapper.cpp, above the extern "C" { line, add:
using my::ABC;
or
using namespace my;
john's suggestion (replace all instances of ABC with my::ABC in ABCWrapper.cpp) also works.
You have to set the scope for your ABC class. So replace all the ABC class as my::ABC except in the class declaration.
extern "C" {
CABC* create_abc() {
my::ABC* abc = new my::ABC();
return (my::ABC*)abc;
}
void call_abc_methods(const CABC *p_abc) {
my::ABC* abc = (my::ABC*)p_abc;
abc->doSomething();
}
void destroy_abc(CABC *p_abc) {
my::ABC* abc = (my::ABC*)p_abc;
delete abc;
}
}
Related
I am writing unit-tests for a C++ class that has dependencies on 3rd-party C and C++ libraries, as well as 1st-party C libraries. I am running into trouble, because I want the class under test to consume a mocked 3rd-party C library and the test runner to consume the REAL 3rd-party C library.
I am writing unit-tests, and the class under test has a dependency on libx.
I have created a libmockx which allows me to test parameters and inject return values.
The class under test, needs to link to libmockx so I can inspect and control it's behavior.
The unit test application, needs to link to libx in order to formulate/parse libx data types.
Which pattern or method is used to link the test runner to libx, the class under test to libmockx, then link the test-runner to the class under test? Many solutions discuss letting the linker do the "dirty work", but ld has 100s of parameters and I don't know how to make it work.
Currently, I have redefinition errors for all the mock implementations, and I need a way to work around it (whether that involves the linker or not).
EDIT BELOW: (in response to comments)
Imagine 7 files:
test.cpp - the test runner
object.cpp - the object under test
object.hpp - the object header
mock-parameters.h - provide mock parameter access to tests
mock-x.c - the mock implementation of x
x.c - the x implementation
x.h - the x header
Before I needed to instantiate and manipulate x objects, I was able to compile my test with a single call to g++:
g++ test.cpp object.cpp mock_x.c
I am trying to add a test to test.cpp, that will supply and test an x object result value. Now, I need to link test.cpp against x.c while still linking object.cpp against mock_x.c.
When I add x.c to the compilation list, I get the following (expected) error:
/usr/bin/ld: /tmp/ccDVQxtN.o: in function `set_foo(X*, int)':
mock-x.c:(.text+0x0): multiple definition of `set_foo(X*, int)'; /tmp/cc3t8CjP.o:x.c:(.text+0x14): first defined here
collect2: error: ld returned 1 exit status
x.h
#ifndef X_H
#define X_H
typedef struct X {
int foo;
char bar;
} X;
X * create_x (void);
int set_foo (X *, int);
char set_bar (X *, char);
void delete_x (X *);
#endif // X_H
x.c
#include "x.h"
#include "stdlib.h"
X * create_x (void) {
return (X *)malloc(sizeof(X));
}
int set_foo (X * x, int i) {
x->foo = i;
return i;
}
char set_bar (X * x, char c) {
x->bar = c;
return c;
}
void delete_x (X * x) {
free(x);
}
mock-x.c
#include "x.h"
#include "mock-parameters.h"
Set_foo_params set_foo_params;
int set_foo (X * x, int i) {
// Stash parameter(s)
set_foo_params.x = x;
set_foo_params.i = i;
return set_foo_params.result;
}
object.hpp
#ifndef OBJECT_HPP
#define OBJECT_HPP
#include "x.h"
class Object {
public:
void embed_x(X *);
int increment_foo(int);
private:
X * _x;
};
#endif // OBJECT_HPP
object.cpp
#include "object.hpp"
void Object::embed_x (X * x) {
_x = x;
}
int Object::increment_foo (int i) {
++i;
return set_foo(_x, i);
}
mock-parameters.h
#include "x.h"
typedef struct Set_foo_params {
X * x;
int i;
int result;
} Set_foo_params;
extern Set_foo_params set_foo_params;
test.cpp
#include "object.hpp"
#include "mock-parameters.h"
int test_object_update_foo_correctly_invokes_x_set_foo (void) {
int result;
Object object;
// Setup
X * x = create_x();
object.embed_x(x);
// Execute
object.increment_foo(7);
// Test
if (8 == set_foo_params.i) {
result = 0;
} else {
result = 1;
}
delete_x(x);
return result;
}
int test_object_update_foo_correctly_returns_x_set_foo_result (void) {
int result;
Object object;
// Setup
X * x = create_x();
object.embed_x(x);
set_foo_params.result = 5;
// Execute
int output = object.increment_foo(0);
// Test
if (5 == output) {
result = 0;
} else {
result = 2;
}
delete_x(x);
return result;
}
int main (void) {
int result;
result |= test_object_update_foo_correctly_invokes_x_set_foo();
result |= test_object_update_foo_correctly_returns_x_set_foo_result();
return result;
}
Since you clarified that your object.c and test.c use the same functions - but should use different implementations - the only way is to separate those functions in some way. According to ODR (One Definition Rule of C++ programming language) you cannot have two different implementations of the same function/object.
So the only way is to create two separate functions/objects (with a separate names). You can use different names or put the same names in different namespaces.
Example:
x.h:
#ifndef X_H
#define X_H
#ifdef X_DEBUG
#define X mock_x
#else
#define X x
#endif
namespace X {
int foo();
}
#endif // X_H
x.cpp:
namespace x {
int foo()
{
int res = -1;
// rightful implementation
return res;
}
}
mock-x.cpp:
namespace mock_x {
int foo()
{
int res = -1;
// mock implementation
return res;
}
}
object.cpp:
#define X_DEBUG
#include <x.h>
int obj_bar()
{
int res = X::foo(); // uses mock implementation
return res;
}
}
test.cpp:
#include <x.h>
int test_bar()
{
int res = X::foo(); // uses real implementation
return res;
}
}
Obviously this approach is scalable - you can put any number of classes/function/objects in this namespace X. Common functionality may be defined inline in the header <x.h> and separate functionality in corresponding modules x.c and mock-x.c.
In general:
I would definitely look in the direction of creating mock-x implementation as a distinct set of linker symbols. This is definitely much more controllable and flexible approach. You can even use original x API implementation from your mock-x functionality - e.g. as a fallback when appropriate.
EDIT:
For your specific newly added example code.
mock-x.c:
#include "x.h"
#include "mock-parameters.h"
Set_foo_params set_foo_params;
int mock_set_foo (X * x, int i) {
// Stash parameter(s)
set_foo_params.x = x;
set_foo_params.i = i;
return set_foo_params.result;
}
The rest of the files are unchanged.
GCC should compile object.cpp separately with command:
g++ -Dset_foo=mock_set_foo -c object.cpp -o object.o
g++ test.cpp object.o x.c mock_x.c
GCC option -Dset_foo=mock_set_foo works the same as
#define set_foo mock_set_foo
in the beginning of each source file in command line after this option.
This option is widely used for compile-time configuration purposes.
Let say I've decleared this within MyTools.h
#ifndef _MYTOOLS_
#define _MYTOOLS_
typedef struct {
// const
double LN20;
double LN40;
// methods
double NoteToFrequency(int noteNumber);
} Tool;
extern const Tool tool;
#endif // !_MYTOOLS_
For every compilation unit, there is only a global/const/unique instance of Tool. Exactly what I want.
But now: how can I define it? In the .h i've only declared it. How can I define it in .cpp? Tried somethings like:
tool.LN20 = 1.34;
But of course it doesn't works. And the method's definition?
extern doesn't define any variable it just declares it. What you wan't to achieve can be done as below:
The link Global const object shared between compilation units explains how to do it with extern const
t.h file
#ifndef _MYTOOLS_
#define _MYTOOLS_
struct Tool {
// const
double LN20;
double LN40;
double NoteToFrequency(int noteNumber);
} ;
extern const Tool tool ;
#endif // !_MYTOOLS_
t1.cpp
#include "t.h"
#include <stdio.h>
void use_tool()
{
printf("%f\n",tool.LN20);
printf("%f\n",tool.LN40);
return;
}
t2.cpp
#include "t.h"
#include <stdio.h>
const Tool tool = {.LN20 = 20.0, .LN40 = 30.2};
double Tool::NoteToFrequency(int noteNumber)
{
return 12.0;
}
void use1_tool()
{
printf("%f\n",tool.LN20);
printf("%f\n",tool.LN40);
return;
}
int main()
{
void use_tool();
use_tool();
use1_tool();
return 0;
}
Hope this helps.
I'm trying to call a function defined in a C file from my CPP code and I think I am having issues getting the correct namespace. When compiling I get the error: "Undefined reference to 'Get'".
My C header:
// c.h
#ifndef C_H
#define C_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
VAL_A1,
VAL_A2
} TYPE_A;
typedef enum
{
VAL_B1,
VAL_B2
} TYPE_B;
typedef enum
{
VAL_C1,
VAL_C2
} TYPE_C;
typedef struct
{
TYPE_B b;
TYPE_C c;
} TYPE_D;
TYPE_A Get(TYPE_B b, TYPE_D *d);
#ifdef __cplusplus
}
#endif
#endif
And my CPP file:
// main.cpp
...
extern "C" {
#include "c.h"
}
...
namespace MyNamespace
{
...
MyClass::MyFunc()
{
TYPE_D d;
// None of these calls will compile
// Get(VAL_B1, &d);
// ::Get(VAL_B1, &d);
}
...
}
I have tried calling without namespace reference and also with the "root" namespace using "::" with no luck. Any help is appreciated. I've read through this which seems to clarify it but I don't really understand it:
using C++ with namespace in C
"Undefined reference" means that the function has been declared (in the header), but not defined. You'll need to define the function in a source file somewhere (presumably the C file you refer to), and make sure that is linked when you build the program.
First, let's note what that error means. An undefined reference at the linker stage means that the compiler is unable to find the instance of something. In this case, the implementation of a function.
Let's look at your code.. There are a few things missing that we need to add to make it compilable:
A definition for Get().
main()
The class definition for MyClass.
Once we added those three fixes, the code compiles without error.
extern "C" { extern "C" {
typedef enum {
VAL_A1,
VAL_A2
} TYPE_A;
typedef enum {
VAL_B1,
VAL_B2
} TYPE_B;
typedef enum {
VAL_C1,
VAL_C2
} TYPE_C;
typedef struct {
TYPE_B b;
TYPE_C c;
} TYPE_D;
TYPE_A Get(TYPE_B b, TYPE_D *d) {
return VAL_A1;
}
}}
namespace MyNamespace {
struct MyClass {
void MyFunc();
};
void MyClass::MyFunc() {
TYPE_D d;
Get(VAL_B1, &d);
::Get(VAL_B1, &d);
}
}
int main() {}
The definition of Get (not shown in the question) also needs to be enclosed in extern "C".
The main difference between C and C++ functions, in practice, is the way they are named in the executable format. C++ functions get "name mangling" treatment by the linker but C functions do not. The linker will see the C++ definition of Get and it will have no idea of its relation to the C declaration, even if they have the same signature.
I am creating programs using C. However, I require to use a lot of libraries that have API's only for C++. So, is it possible that I can create a shared object in C++ and then access its functionality using C?
The only data I would be passing and returning would be C compatible data types.
Converting or migrating to cpp is not an option here.
If it is not possible to interface these codes, how do I get information from C++ code to C code?
I tried calling C++ functions from C, but I get errors during linking when I include <string>. So when I call C++ functions from C, should I only use that code which will be C compiler compatible?
C++ header cppfile.hpp
#ifndef CPPFILE_H
#define CPPFILE_H
#ifdef __cplusplus
extern "C" {
#endif
extern int myfunction(const char *filename);
#ifdef __cplusplus
}
#endif
#endif
C++ file cppfile.cpp
#include "cppfile.hpp"
#include <string>
int myfunction(const char *filename) {
String S(filename);
return 0;
}
C file cmain.c
#include "cppfile.hpp"
int main(int argc, char **argv)
{
int i = myfunction(argv[1]);
printf("%d\n", i);
return 0;
}
Compiling:
gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
You want something more like this (and here I will use a slightly more meaningful example):
C/C++ header - animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
#ifdef __cplusplus
class Animal {
public:
Animal() : age(0), height(0) {}
Animal(int age, float height) : age(age), height(height) {}
virtual ~Animal() {}
int getAge();
void setAge(int new_age);
float getHeight();
void setHeight(float new_height);
private:
int age;
float height; // in metres!
};
#endif /* __cplusplus */
#ifdef __cplusplus
extern "C" {
#endif
struct animal; // a nice opaque type
struct animal *animal_create();
struct animal *animal_create_init(int age, float height);
void animal_destroy(struct animal *a);
void animal_setage(struct animal *a, int new_age);
void animal_setheight(struct animal *a, float new_height);
int animal_getage(struct animal *a);
float animal_getheight(struct animal *a);
#ifdef __cplusplus
}
#endif
#endif /* ANIMAL_H */
C++ animal implementation file - animal.cpp
#include "animal.h"
#define TO_CPP(a) (reinterpret_cast<Animal*>(a))
#define TO_C(a) (reinterpret_cast<animal*>(a))
void Animal::setAge(int new_age) { this->age = new_age; }
int Animal::getAge() { return this->age; }
void Animal::setHeight(float new_height) { this->height = new_height; }
float Animal::getHeight() { return this->height; }
animal *animal_create() {
animal *a = TO_C(new Animal);
return a;
}
animal *animal_create_init(int age, float height) {
animal *a = TO_C(new Animal(age, height));
return a;
}
void animal_destroy(animal *a) {
delete TO_CPP(a);
}
void animal_setage(animal *a, int new_age) {
TO_CPP(a)->setAge(new_age);
}
void animal_setheight(animal *a, float new_height) {
TO_CPP(a)->setHeight(new_height);
}
int animal_getage(animal *a) {
TO_CPP(a)->getAge();
}
float animal_getheight(animal *a) {
TO_CPP(a)->getHeight();
}
C client code - main.c
#include "animal.h"
#include <stdio.h>
int main()
{
// 6'0" 25yo (perhaps a human? :P)
struct animal *a = animal_create(25, 1.83);
animal_setage(a, 26); // birthday
printf("Age: %d\nHeight: %f", animal_getage(a), animal_getheight(a));
animal_destroy(a);
return 0;
}
C++ client code - main.cpp
#include "animal.h"
#include <iostream>
int main()
{
// 6'0" 25yo (perhaps a human? :P)
Animal* a = new Animal(25, 1.83);
a->setAge(26); // birthday
std::cout << "Age: " << a->getAge() << std::endl;
std::cout << "Height: " << a->getHeight();
delete a;
return 0;
}
So when you compile the library, you compile animal.cpp with a C++ compiler. You can then link to it with C code, and use the animal_xxx functions.
Note the use of struct animal and Animal. Animal is a normal C++ type. It's exactly what it looks like. struct animal, on the other hand, is an "opaque" type. That means that your C program can see it's there, and can have one, but it doesn't know what is inside it. All it knows is that it has a function that takes a struct animal*.
In a real library you will want to have customisation points for memory allocation. So assuming this is the library libjungle, you probably want at least jungle_setmalloc and jungle_setfree with sensible defaults. You can then set up the global new and delete in libjungle's C++ code to use these user-defined functions.
This is entirely possible. Here is how, quickly:
1.) You have a header.h with a C API that doesn't include any Cplusiness.
#ifndef MIXEDCCPP_H
#define MIXEDCCPP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h> // Any C-compatible headers will go here.
// C API goes here. C Functions can't contain any CPPiness.
void myclass_setName( void *pClassObj, const char *pName, int nameLen );
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
// Stuff that is only compatible with CPP goes here
// __cplusplus section won't get processed while compiling C files.
#include <vector> // CPP headers.
class MyClass {
// Classes etc.
};
#endif // #ifdef __cplusplus
#endif // MIXEDCCPP_H
Then in the .cpp, you simply create some C-API functions that can even include CPP right in them:
#include "mixedccpp.h"
extern "C" {
// C API goes here. C Functions can't contain any CPPiness in their prototypes.
void myclass_setName( void *pClassObj, const char *pName, int nameLen )
{
// But CPP knowledge can go inside the function - no problem, since this is a CPP file.
MyClass *pMyClass = static_cast<MyClass *>(pClassObj);
pMyClass->setName( pName, nameLen );
}
} // #extern "C"
// CPP Stuff goes here... or vice-versa.
In your case, you don't actually need any CPP code declared in your header since you are calling external libraries. But you need to create C-compatible functions in your CPP file which can call out to CPP libraries. Use extern "C" for those functions that need to be called from C files, and then use C-structs instead of classes and, if classes are needed, use void * to point to them and then cast them back to their class from the C function any time you need to access them. A standard makefile should be able to compile this just fine, assuming it compiles .cpp files as .cpp and understands extern "C" {}
Your C code cannot use the C++ header <string>. You have to ensure that the functions in the C++ API that are to be called from C are declared extern "C" (as you have), and use only types recognized by a C compiler (as you have).
You also need to link with the C++ compiler if any of your code is in C++. You can do it otherwise if you're prepared to spend a lot of energy getting the loader options right, but it is far simpler just to use the C++ compiler:
gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
g++ -o cmain cmain.o cppfile.so
Of course, you need to:
Add #include <stdio.h> in cmain.c.
Use std::string S(filename); in cppfile.cpp.
Also, if the program is invoked without arguments, you get:
$ ./cmain
terminate called throwing an exceptionAbort trap: 6
$ ./cmain x3
0
$
You need to protect against misuse, even in test programs.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
So I'm at a complete loss...
In my code I've got
void Parser(FILE* file)
{
Parser par(file);
par.Parse();
}
and I call it in my main function with
Parser(file);
and the header file I've got (which I included in the main file) looks like:
class Parser: public Lexer
{
public:
Parser(FILE* file):Lexer(file);
int Parse();
};
and the error I'm getting is:
p02.cpp: In function 'void Parser(FILE*)':
p02.cpp:20: error: expected ';' before 'par'
p02.cpp:21: error: 'par' was not declared in this scope
make: *** [p02.o] Error 1
What I don't understand is why it is expecting a semicolon before par. Isn't that a legal declaration of a variable for that class?
Edit2: Changing my function name to not be Parser like the class name does not solve this problem. It does give me an extra error telling me that Parser is not declared in this scope, but I cannot see how that is when I've added the include file containing the Parser class right above the declaration for the function.
Edit: My Files
p02.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <cstring>
#include <string>
#include "p02lex.h"
#include "y.tab.h"
using namespace std;
void Parser(FILE* file)
{
Parser par(file);
par.Parse();
}
int main(int argc, char* argv[])
{
char fileName[255];
switch(argc)
{
case 1:
cout << "Enter the input file name. ";
cin >> fileName;
break;
case 2:
strcpy(fileName, argv[1]);
break;
default:
cout << "Too many arguments!\n";
exit(1);
}
FILE* file = fopen(fileName, "r");
Parser(file);
fclose(file);
return 0;
}
p02lex.l:
#include "p02lex.h"
#define ID 257
...
#define PROGRAM 304
int TokenMgr(int t);
const char* getTokens(int tokenCode);
unsigned lineCount = 1, columnCount = 1;
%}
LETTER [a-z]|[A-Z]
DIGIT [0-9]
%%
// rules defined here, calling TokenMgr()
%%
int TokenMgr(int t)
{
/* int tc = t;
if (t == IDENTIFIER)
{
char s[1024];
ToLower(s, yytext, strlen(yytext));
tc = RW[s];
if (tc == 0)
tc = t;
}
PrintToken(tfs, tc, line, col);
col += yyleng; */ //JEG
printf("Token:Code=%d Name=%10s line=%3u col=%3u Spelling=\"%s\"\n", t, getTokens(t), lineCount, columnCount, yytext);
columnCount += yyleng;
return /* tc */ 0; // JEG
}
Lexer::Lexer(FILE* file)
{
yyin = file;
}
int Lexer::Scan(void)
{
return yylex();
}
const char* getTokens(int tokenCode)
{
switch(tokenCode)
{
case ID:
return "ID";
... // more cases, returning strings
default:
return NULL;
}
}
p02lex.h:
#ifndef p02lex_h
#define p02lex_h 1
#endif
int yylex(void);
class Lexer
{
public:
Lexer(FILE* file);
int Scan(void);
};
p02par.h:
#ifndef p02par_h
#define p02par_h 1
#endif
using namespace std;
#ifdef __cplusplus
extern "C"
#endif
int yyparse(void);
class Parser: public Lexer
{
public:
Parser(FILE* file):Lexer(file){}
void Parse();
// int Scan(void);
};
p02par.y:
#include <stdio.h>
#include "p02lex.h"
#include "p02par.h"
void yyerror(const char* m);
%}
%token PROGRAM
%token ID
%token SEMICOLON
%%
program:
PROGRAM ID SEMICOLON
{ printf("Stuff happens!\n"); }
%%
void yyerror(const char* m)
{
printf("%s\n", m);
}
/*Parser::Parser(FILE* file):Lexer(file)
{
}*/
int Parser::Parse()
{
return yyparse();
}
p02make:
#LEX = flex
#YACC = yacc -d
CC = g++
OBJ = p02.o p02par.o p02lex.o
p02: ${OBJ}
$(CC) -o p02 ${OBJ} -ll -ly
y.tab.h p02par.cpp: p02par.y
yacc -d -v p02par.y
mv y.tab.c p02par.cpp
p02lex.cpp: p02lex.l
lex p02lex.l
mv lex.yy.c p02lex.cpp
p02par.o: p02par.cpp p02par.h
$(CC) -c -g p02par.cpp
p02.o: p02.cpp p02lex.h p02par.h
$(CC) -c -g p02.cpp
p02lex.o: p02lex.cpp p02lex.h y.tab.h
$(CC) -c -g p02lex.cpp
that should be :
Parser(File* file):Lexer(file) {}
Wait I checked that code, rename the function: void Parser(FILE *f) to something else.
You can only include an initializer list in a constructor definition, not a constructor declaration, so it must be followed by a function body (often empty), not a semicolon. There's also a bit of a problem with a name conflict:
void Parser(FILE* file) // here you've defined Parser as the name of a function
{
Parser par(file); // but here you're trying to use it as the name of a class.
par.Parse();
}
Edit: Here's a bit of code that compiles cleanly, at least with the compilers I have handy:
#include <stdio.h>
class Lexer {
FILE *infile;
public:
Lexer(FILE *f) : infile(f) {}
};
class Parser : public Lexer {
public:
Parser(FILE *f) : Lexer(f) {}
void Parse() {}
};
void do_parse(FILE *file) {
Parser p(file);
p.Parse();
}
You need to qualify Parser because the function has the same identifier as the class:
void Parser(FILE* file)
{
class Parser par(file);
par.Parse();
}
You could also rename the function.
You also need braces here:
Parser(FILE* file):Lexer(file) {}
Change
void Parser(FILE* file)
to
Parser::Parser(FILE* file)
Constructors do not have a return type.