c++ call object method using pointer - c++

I created a shared lib, in which I defined a class Foo. In class Foo, there is a function named sayHi(). My question is once I created a Foo object foo, how can I call sayHi() of it. I put my code here.
mylib.h
#ifndef FUNTEST_MYLIB_H
#define FUNTEST_MYLIB_H
class Foo {
public:
int id = 0;
void sayHi();
};
#endif //FUNTEST_MYLIB_H
mylib.cpp
#include <iostream>
#include "mylib.h"
using namespace std;
void Foo::sayHi()
{
cout << "Implemented by lib" << endl;
cout << "id is: " << id << endl;
}
Foo* create()
{
return new Foo();
}
then I use the following commands compile the shared lib:
g++ -c -std=gnu++11 -fPIC mylib.cpp
g++ -shared -fPIC -o mylib.so mylib.o
At the client side, I write two files:
mylib2.cpp
#include <iostream>
#include "mylib.h"
using namespace std;
void Foo::sayHi() {
cout << "Implemented by caller" << endl;
cout << "id is: " << id << endl;
}
main.cpp
#include <iostream>
#include <dlfcn.h>
#include "mylib.h"
using namespace std;
Foo* (*create)();
void (*sayHi)();
int main() {
void* lib = dlopen("./mylib.so", RTLD_LAZY);
create = (Foo* (*)())dlsym(lib, "_Z6createv");
sayHi = (void (*)())dlsym(lib, "_ZN3Foo5sayHiEv");
Foo* foo = create();
sayHi();
foo->sayHi();
foo->id = 100;
cout << "Set id to " << foo->id << endl;
/*
*
* how can I make the follow statement 'sayHi();'
* output the following content:
* Implemented by lib
* id is: 100
*/
sayHi(); // line 29, FIXME
foo->sayHi();
return 0;
}
compile the client with following command:
g++ -std=gnu++11 main.cpp mylib.h mylib2.cpp -ldl
finally run the client:
./a.out
the output is:
Implemented by lib
id is: 0
Implemented by caller
id is: 0
Set id to 100
Implemented by lib
id is: -1407102376 // FIXME
Implemented by caller
id is: 100
how can I make the statement 'sayHi();' at line 29 output the following content:
Implemented by lib
id is: 100
I know if i call the sayHi() function directly, without the foo object, the output will be wrong. But how can I fix it?

The declaration of the sayHi global variable in main.cpp is wrong. You're declaring it as a function pointer. However, since it points to a member function, it needs to be a member function pointer.
void (Foo::*sayHi)();
Then when you call it you need a Foo object:
(foo->*sayHi)();
Alternatively, if you make the sayHi method virtual, you shouldn't need to load the symbol for it at all, since that will be effectively handled by the vtable.

Related

Accessing a nested class member from another nested class (C++)

I have a class Enclosing, that contains PImpl class.
The declaration of the Enclosing is in all_includes.h file and it's definition is in enclosing.cpp. I can not change that. PImpl is defined in enclosing.h.
I want to add a Nested class into the Enclosing and I want to be able to access from it a member of a PImpl.
Is that even possible?
For now I can not access Enclosing::Nested from main.
I tried different configurations of where to declare and define Nested, but I didn't manage to do it so it works.
The code:
all_includes.h
#ifndef _TVG_ALL_INCLUDES_H_
#define _TVG_ALL_INCLUDES_H_
#include <iostream>
using namespace std;
struct Enclosing
{
int _e;
Enclosing(int i);
class PImpl;
PImpl* _p;
class Nested;
Nested fun();
};
#endif
enclosing.cpp
#include "enclosing.h"
#include <iostream>
Enclosing::Enclosing(int i) : _e{i}, _p{new PImpl(2*i)}
{
cout << "Enclosing constr: " << _e << endl;
}
Enclosing::Nested Enclosing::fun()
{
cout << "returns Enc::Nest " << endl;
return Enclosing::Nested(0, nullptr);
}
enclosing.h
#include "all_includes.h"
class Enclosing::PImpl
{
int _ep;
public:
PImpl(int i) : _ep{i}
{
cout << "PImpl constr: " << _ep << endl;
}
friend Enclosing::Nested;
};
class Enclosing::Nested
{
int _en;
Enclosing* _ptr_enc;
public:
Nested(int i, Enclosing* p) : _en{i}, _ptr_enc{p}
{
cout << "Nested constr: " << _en << endl;
}
void acces() // this function is my goal - I want to access a PImpl member from Nested
{
cout << "PImpl private member: " << _ptr_enc->_p->_ep << endl;
}
};
main.cpp
#include "all_includes.h"
int main()
{
Enclosing* c = new Enclosing(17);
//Enclosing::Nested n(22, c) ; //comilator error: has initializer but incomplete type
return 0;
}
Makefile
CC=g++
CCFLAGS=-std=c++14 -Wall
main: main.o enclosing.o
${CC} ${CCFLAGS} -o main main.o enclosing.o
main.o: main.cpp
${CC} ${CCFLAGS} -c main.cpp
class1.o: enclosing.cpp
${CC} ${CCFLAGS} -c enclosing.cpp
While compiling main(), compiler can't figure out the details of class Nested. This is because there is only a forward declaration of class available. How will the function signature be verified?
Try including the other header file.
This is what I get:
./main
PImpl constr: 34
Enclosing constr: 17
Nested constr: 22

Using singleton in delayed loaded shared library

I have a static C++ library that defines a singleton class. The static member variable of the singleton is a std::unique_ptr. I also have a shared library that defines a plugin which is delayed-loaded by the main application (using dlopen). Both the main application and the shared library link to the static library and make use of its singleton. All parts are compiled using compiler flags -fPIC and -rdynamic using GCC 7.5.0. The shared library and the executable are not linked at link-time.
At run-time, all components seem to make correct use of the same singleton instance. However, even though the constructor of the singleton class is only called once, its destructor is called twice, resulting in a double delete and therefore a segfault. The destructor seems to be called once for each compilation unit it is used in. If the shared library is linked to the main application at link time, this does not happen.
This issue occurred to me first when trying to use Poco::Logger from the C++ Poco library as the static library.
I looked at the question posed here and tried the example (using GCC 7.5.0) replacing the raw pointer with a std:unique_ptr. This results in the same double delete. The only way I seem to be able to prevent the double delete is to link the main application to the shared library at link-time and remove the direct link of the shared library to the static library. This would ensure only 1 copy of the shared library exists at run time. However, I wonder if that would be a good solution (besides that I don't seem to be able to do that through CMake).
Linking the shared library to the main application does not seem to make sense, since not all plugins will be known at compile time and this would defy the purpose of a plug in.
The following minimal example has been based on the example from bourneli
The static library contains the following files:
/*
* singleton.h
*
* Based on: bourneli
* Adaptation: mojoritty
*/
#ifndef SINGLETON_H_
#define SINGLETON_H_
#include <memory>
class singleton
{
private:
singleton();
static std::unique_ptr<singleton> pInstance;
public:
~singleton();
static singleton& instance();
public:
int num;
};
#endif /* SINGLETON_H_ */
and
/*
* singleton.cpp
*
* Based on: bourneli
* Adaptation: mojoritty
*/
#include "singleton.h"
#include <iostream>
singleton::singleton()
{
std::cout << "Constructed " << this << std::endl;
num = -1;
}
singleton::~singleton()
{
std::cout << "Destroyed" << this << std::endl;
}
static singleton& singleton::instance()
{
if (!pInstance)
{
pInstance.reset(new singleton());
}
return *pInstance;
}
std::unique_ptr<singleton> singleton::pInstance;
The shared library contains the following files:
// plugin.h
#include "singleton.h"
#include <iostream>
extern "C" void hello();
and
// plugin.cpp
#include "plugin.h"
void hello()
{
std::cout << "singleton.num in hello.so : " << singleton::instance().num << std::endl;
++singleton::instance().num;
std::cout << "singleton.num in hello.so after ++ : " << singleton::instance().num << std::endl;
}
Finally, the main application contains the following code:
/* main.cpp
*
* Author: bourneli
* Adaptation: mojoritty
*/
#include <iostream>
#include <dlfcn.h>
#include "singleton.h"
int main() {
using std::cout;
using std::cerr;
using std::endl;
singleton::instance().num = 100; // call singleton
cout << "singleton.num in main : " << singleton::instance().num << endl;// call singleton
// open the library
void* handle = dlopen("./libplugin.so", RTLD_LAZY);
if (!handle) {
cerr << "Cannot open library: " << dlerror() << '\n';
return 1;
}
// load the symbol
typedef void (*hello_t)();
// reset errors
dlerror();
hello_t hello = (hello_t) dlsym(handle, "hello");
const char *dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol 'hello': " << dlerror() << '\n';
dlclose(handle);
return 1;
}
hello(); // call plugin function hello
cout << "singleton.num in main : " << singleton::instance().num << endl;// call singleton
dlclose(handle);
}
Building and running this application results in the follwing terminal output:
created 0x563018c48e70
singleton.num in main : 100
singleton.num in hello.so : 100
singleton.num in hello.so after ++ : 101
singleton.num in main : 101
destroyed 0x563018c48e70
destroyed 0x563018c48e70
free(): double free detected in tcache 2
Aborted (core dumped)

Clarification with accessing non-static class member function via pointer

I am having difficulty accessing a non-static member function from a function pointer and can't quite figure out my syntax issue. When attempting to compile as seen below I receive "error: fnc_ptr not declared in this scope." and when if the code is modified to not access the function it should point to it compiles and will print out 1 for bar.fn_ptr .To compile I used:
g++ -std=c++11 -Wall example.cpp foo.cpp
The split file structure/namespace is just meant to emulate the same conditions as my original issue.
example.cpp
#include "foo.h"
#include <iostream>
int main(int argc, char* argv[]){
pizza::foo bar;
bar.fn_ptr = &pizza::foo::fnc_one;
std::cout << (bar.*fn_ptr)(1) << std::endl;
return 0;
}
foo.cpp
#include <cmath>
#include "foo.h"
namespace pizza{
double foo::fnc_one(double x){
return pow(x,3) - x + 2;
}
}
foo.h
namespace pizza{
class foo{
public:
double (foo::*fn_ptr)(double);
double fnc_one(double);
foo(){
fn_ptr = 0;
}
};
}
A very similar question can be found here, with additional reference here.
You are missing bar. when referring to fn_ptr which is an attribute of that object. Change it to:
std::cout << (bar.*(bar.fn_ptr))(1) << std::endl;
And it works.
I also recommend reading this FAQ on the subject: https://isocpp.org/wiki/faq/pointers-to-members
I believe the correct syntax is:
//std::cout << (bar.*fn_ptr)(1) << std::endl;
std::cout << (bar.*(bar.fn_ptr))(1) << std::endl;

Passing a function pointer from C code to a C++ library as an argument

What is the correct way to pass a function pointer from C code to a C++ function as an argument? Simple example:
foo_c.h
typedef int (*int_func)(int);
extern void foo(int_func);
foo.h
#ifdef __cplusplus
extern "C" {
#endif
#include "foo_c.h"
void foo(int_func);
#ifdef __cplusplus
}
#endif
foo.cpp
#include "foo.h"
#include <iostream>
void foo(int_func)
{
std::cout << "foo : int_func(5) : " << int_func(5) << std::endl;
}
main.c
#include <stdio.h>
#include "foo_c.h"
int timestwo(int x)
{
return x*2;
}
int main()
{
foo(timestwo);
return 0;
}
Makefile
all: main.c libfoo.so
gcc main.c -L`pwd` -lfoo
libfoo.so: foo.cpp foo.h foo_c.h
g++ -fPIC -shared -o libfoo.so foo.cpp
clean:
rm -rf a.out libfoo.so
This code compiles and runs, but gets the incorrect output:
foo : int_func(5) : 1
What's going on here?
This code:
void foo(int_func)
You have a variable type but no name, and you are not calling the function.
Change it to:
void foo(int_func myfunc)
You would have realized your function wasn't being called if you had added some debug output to it:
int timestwo(int x)
{
std::cout << "timestwo(" << x << ")" << std::endl;
return x*2;
}
In your version the output doesn't happen, so the function wasn't called, so int_func was not being interpreted as a function.
Your problem is here:
void foo(int_func)
{
std::cout << "foo : int_func(5) : " << int_func(5) << std::endl;
}
You're not calling the function.
Change it to:
void foo(int_func fn)
{
std::cout << "foo : int_func(5) : " << fn(5) << std::endl;
}

Multiple Symbol Reference problem in shared library of a factory design pattern

I am trying to write a C++ implementation of factory design pattern. I would also like to do it using shared objects and dynamic loading. I am implementing a function called new_animal() which is passed a string. If the string is "dog", then it needs to see if a class dog is registered in shared object and create a dog object. If the string is "cat" it needs to find registered class cat and return an object of it. The function new_animal() does not know ahead of the time what strings will be passed to it. Thus, it would error out if a string with corresponding unregistered class is passed. Here is the code -
creator.hpp -
#ifndef CREATOR_HPP
#define CREATOR_HPP
#include <string>
class Animal {
public :
virtual string operator() (const string &animal_name) const = 0;
virtual void eat() const = 0;
virtual ~Animal() { }
};
class AnimalCreator {
public :
// virtual Animal *create() const = 0;
virtual ~AnimalCreator() { }
};
typedef Animal* create_animal_t();
typedef void destroy_animal_t(Animal *);
#endif
cat.hpp -
#ifndef CAT_HPP
#define CAT_HPP
#include "creator.hpp"
#include <iostream>
#include <string>
class cat : public Animal {
public :
string operator() (const string &animal_name) const { return "In cat () operator"; }
void eat() const { cout << "cat is eating" << endl; }
};
class catCreator : public AnimalCreator {
public :
}theCatCreator;
#endif
cat.cpp -
#include "cat.hpp"
#include <iostream>
using namespace std;
extern "C" Animal *create() {
cout << "Creating cat ..." << endl;
return new cat;
}
extern "C" void destroy(Animal* a) {
delete a;
}
dog.hpp -
#ifndef DOG_HPP
#define DOG_HPP
#include <string>
#include "creator.hpp"
class dog : public Animal {
public:
string operator() (const string &animal_name) const { return "In dog"; }
void eat() const { cout << "Dog is eating" << endl; }
};
class dogCreator : public AnimalCreator {
public:
}theDogCreator;
#endif
dog.cpp -
#include "dog.hpp"
#include <iostream>
using namespace std;
extern "C" Animal *create() {
cout << "Creating dog" << endl;
return new dog;
}
extern "C" void destroy(Animal *aa) {
delete aa;
}
main.cpp -
#include "creator.hpp"
#include "cat.hpp"
#include "dog.hpp"
#include <iostream>
#include <string>
#include <map>
#include <dlfcn.h>
map<string, AnimalCreator *> AnimalMap;
void initialize() {
AnimalMap["dog"] = &theDogCreator;
AnimalMap["cat"] = &theCatCreator;
}
Animal * new_animal(const string &animal) {
static bool isInitialised (false);
if (!isInitialised) {
initialize();
isInitialised = true;
}
AnimalCreator *theAnimalCreator = AnimalMap[animal];
if (!theAnimalCreator) {
cout << "error: " << animal << " not registerd" << endl;
exit(1);
}
Animal *theAnimal = theAnimalCreator->create();
return theAnimal;
}
int main() {
void *animal = dlopen("animal", RTLD_LAZY);
if (!animal) {
cout << "error is dlopen" << endl;
exit(1);
}
create_animal_t* new_animal = (create_animal_t*) dlsym(animal, "create");
if (!new_animal) {
cout << "error is dlsym create" << endl;
exit(1);
}
destroy_animal_t* destroy_animal = (destroy_animal_t*) dlsym(animal, "destroy");
if (!destroy_animal) {
cout << "error is dlsym destroy" << endl;
exit(1);
}
Animal *a = new_animal("dog");
Animal *b = new_animal("cat");
a->eat();
b->eat();
destroy_animal(a);
destroy_animal(b);
dlclose(animal);
return 0;
}
Makefile -
# macros
CC = g++
CFLAGS = -g -Wall
MODFLAGS = -fpic -shared
LDFLAGS = -ldl
OBJECTS = main.o animal
# targets
all: foo
foo: $(OBJECTS)
$(CC) -o foo $(OBJECTS) $(LDFLAGS)
animal: dog.cpp cat.cpp
$(CC) $(CFLAGS) $(MODFLAGS) dog.cpp cat.cpp -o animal
clean:
rm -f foo $(OBJECTS)
when I create a shared object using make animal, this is what I get -
bash-2.05$ make animal
g++ -g -Wall -fpic -shared dog.cpp cat.cpp -o animal
ld: fatal: symbol `create' is multiply-defined:
(file /var/tmp/ccgDUpwo.o type=FUNC; file /var/tmp/ccv0VjHp.o type=FUNC);
ld: fatal: symbol `destroy' is multiply-defined:
(file /var/tmp/ccgDUpwo.o type=FUNC; file /var/tmp/ccv0VjHp.o type=FUNC);
ld: fatal: File processing errors. No output written to animal
collect2: ld returned 1 exit status
make: *** [animal] Error 1
I understand that there are multiple definitions of method create() and destroy() and hence the error. But at the same time, I could not use any class specific create() method in main.cpp because doing that will not make it generic. I am keeping create() and destroy() functions outside the class definition. I am also using extern "C" to make sure that compiler does not add name mangling, and keeps symbol name in shared libraries same as the function name.
Can somebody please give me some hints about how to go about this problem ? Any changes that could be done in the class design ?
Thanks for being patient in reading the code above.
- Onkar Deshpande
THe problem is exactly what the linker tells you: how to choose a create function between the two offered definitions?
So you could make it easier on the linker by using different symbols. You can manually choose for e.g. dog_create, cat_create, but you could also just write a template function and instantiate that for the cat and dog types.
// an abstract base factory class
struct Creator {
virtual Animal* create() const = 0;
virtual ~Creator(){};
}
template<typename tAnimal> struct TCreator : public Creator {
tAnimal* create() const { return new tAnimal(); }
// note that the return type can be covariant in C++
}
...
map<string, Creator*> AnimalMap;
void initialize() {
AnimalMap["dog"] = new TCreator<dog>;
AnimalMap["cat"] = new TCreator<cat>;
}
This way, you won't need a create method per animal.
When calling dlsym(), you could dynamically create the name of a function instead of using the fixed name "create". By prepending the name of the object you're trying to create, you could have your main program look for dog_create or cat_create.
I also noticed that your call to new_animal passes a char *, yet the definitions of the corresponding create functions take no parameters. You will want to ensure that these are consistent (that is, both the functions and the calls to them use the same types of parameters).
Now next to the template-based answer I provided, you can also solve the "two symbols for the linker" problem by not linking the cat.cpp and dog.cpp into one shared "animal" object. I guess that's what you ultimately want to do: being able to add animal types at runtime.
This can be achieved by creating a dog.so and cat.so, and later on a swan.so, with each just one create function, which boils down to reordering your makefile to `make a separate target for the cat.cpp and dog.cpp. Maybe you could create a rule for "ANIMALOBJECTS" next to the main object.
Then your main function has to load the create and destroy symbols from each shared object separately.
You mixed two proposed solutions: either use the extern "C" linkage for the create and destroy functions - if you want runtime flexibility (e.g. loading more modules on-the-fly), or use a compiled-in Factory Function, where you don't need to dynamically load symbols at all.
Now you ended up with two shared objects that export no creator objects at all: the code
struct X { int i; double d; } theX;
only declares a symbol theX to whatever cpp file includes the header containing it.
It should also be defined somewhere, either in the X.cpp file, or, preferably, using the proposed template class, in the main/creators.cpp file.
But my guess is you want the dynamic behaviour, so this is my proposal:
go back to the extern "C" Animal* create() version
keep your makefile to have a "dog" and a "cat" shared object
xtofl and Greg,
Thanks a lot for your replies. It would not have been possible without your suggestions. One last time, I am eating a lot of place on this page to share the final code. I really appreciate your active participation in answering my doubts.
creator.hpp -
#ifndef CREATOR_HPP
#define CREATOR_HPP
#include <string>
class Animal {
public :
virtual void eat() const = 0;
virtual ~Animal() { }
};
class AnimalCreator {
public :
virtual Animal *create(const string &) = 0;
virtual ~AnimalCreator() { }
};
typedef Animal* create_animal_t(const string &);
typedef void destroy_animal_t(Animal *);
typedef void eat_animal_t();
#endif
cat.hpp -
#ifndef CAT_HPP
#define CAT_HPP
#include "creator.hpp"
#include <iostream>
#include <string>
class cat : public Animal {
public :
virtual void eat() const;
};
class catCreator : public AnimalCreator {
public :
Animal *create(const string &);
void destroy(Animal *);
}theCatCreator;
#endif
cat.cpp -
#include "cat.hpp"
#include <iostream>
using namespace std;
extern "C" Animal *create_extern_cat(const string &str) {
cout << "Creating " << str << "..." << endl;
return new cat;
}
extern "C" void destroy_extern_cat(Animal* a) {
cout<< "Destroying cat"<< endl;
delete a;
}
extern "C" void eat_extern_cat() {
cout << "Cat is eating" << endl;
}
Animal * catCreator :: create(const string &animal_name) {
return create_extern_cat(animal_name);
}
void catCreator :: destroy(Animal *aa) {
destroy_extern_cat(aa);
}
void cat :: eat() const {
eat_extern_cat();
}
dog.hpp -
#ifndef DOG_HPP
#define DOG_HPP
#include <string>
#include "creator.hpp"
class dog : public Animal {
public:
virtual void eat() const;
};
class dogCreator : public AnimalCreator {
public:
Animal *create(const string &);
void destroy(Animal *);
}theDogCreator;
#endif
dog.cpp -
#include "dog.hpp"
#include <iostream>
using namespace std;
extern "C" Animal *create_extern_dog(const string &str) {
cout << "Creating " << str << "..." << endl;
return new dog;
}
extern "C" void destroy_extern_dog(Animal *aa) {
cout<<"destroying dog" << endl;
delete aa;
}
extern "C" void eat_extern_dog() {
cout << "dog is eating" << endl;
}
Animal * dogCreator::create(const string &animal_name) {
return create_extern_dog(animal_name);
}
void dogCreator::destroy(Animal *a) {
destroy_extern_dog(a);
}
void dog :: eat() const{
eat_extern_dog();
}
main.cpp -
#include "creator.hpp"
#include "cat.hpp"
#include "dog.hpp"
#include <iostream>
#include <string>
#include <map>
#include <dlfcn.h>
map<string, AnimalCreator *> AnimalMap;
void initialize() {
AnimalMap["dog"] = &theDogCreator;
AnimalMap["cat"] = &theCatCreator;
}
Animal * new_animal(const string &animal_tt) {
static bool isInitialised (false);
if (!isInitialised) {
initialize();
isInitialised = true;
}
AnimalCreator *theAnimalCreator = AnimalMap[animal_tt];
if (!theAnimalCreator) {
cout << "error: " << animal_tt << " not registerd" << endl;
exit(1);
}
Animal *theAnimal = theAnimalCreator->create(animal_tt);
return theAnimal;
}
int main() {
destroy_animal_t* destroy_class;
eat_animal_t* eat_class;
Animal* (*register_class)(const string &);
void *animal_cat = dlopen("cat", RTLD_LAZY);
if (!animal_cat) {
cout << "error is dlopen" << endl;
exit(1);
}
register_class = (create_animal_t *) dlsym(animal_cat, "create_extern_cat");
if (!register_class) {
cout << "error is dlsym create" << endl;
exit(1);
}
destroy_class = (destroy_animal_t*) dlsym(animal_cat, "destroy_extern_cat");
if (!destroy_class) {
cout << "error is dlsym destroy" << endl;
exit(1);
}
eat_class = (eat_animal_t*) dlsym(animal_cat, "eat_extern_cat");
if (!eat_class) {
cout << "error in dlsym eat" << endl;
exit(1);
}
Animal *b = new_animal("cat");
b->eat();
(*destroy_class)(b);
dlclose(animal_cat);
void *animal_dog = dlopen("dog", RTLD_LAZY);
if (!animal_dog) {
cout << "error is dlopen" << endl;
exit(1);
}
register_class = (create_animal_t *) dlsym(animal_dog, "create_extern_dog");
if (!register_class) {
cout << "error is dlsym create of dog" << endl;
exit(1);
}
destroy_class = (destroy_animal_t*) dlsym(animal_dog, "destroy_extern_dog");
if (!destroy_class) {
cout << "error is dlsym destroy" << endl;
exit(1);
}
eat_class = (eat_animal_t*) dlsym(animal_dog, "eat_extern_dog");
if (!eat_class) {
cout << "error in dlsym eat" << endl;
exit(1);
}
Animal *a = new_animal("dog");
a->eat();
(*destroy_class)(a);
dlclose(animal_dog);
return 0;
}
Makefile -
# macros
CC = g++
CFLAGS = -g -Wall
MODFLAGS = -fpic -shared
LDFLAGS = -ldl
OBJECTS = main.o dog cat
# targets
all: foo
foo: $(OBJECTS)
$(CC) -o foo $(OBJECTS) $(LDFLAGS)
dog: dog.cpp
$(CC) $(CFLAGS) $(MODFLAGS) dog.cpp -o dog
cat: cat.cpp
$(CC) $(CFLAGS) $(MODFLAGS) cat.cpp -o cat
clean:
rm -f foo $(OBJECTS)
Output -
bash-2.05$ echo $LD_LIBRARY_PATH
/opt/csw/lib:/usr/local/lib/gtk-2.0/2.4.0/immodules:/usr/local/lib/gtk-2.0/2.4.0/loaders:/usr/local/lib/gtk-2.0/2.4.0/engines:/usr/local/lib:/usr/lib:/usr/openwin/lib:/usr/dt/lib:/opt/sfw/lib:/opt/local/SUNWspro/lib
bash-2.05$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
bash-2.05$ ls
cat cat.hpp dog dog.hpp main.cpp Makefile
cat.cpp creator.hpp dog.cpp foo main.o README
bash-2.05$ make clean
rm -f foo main.o dog cat
bash-2.05$ make
g++ -c -o main.o main.cpp
g++ -g -Wall -fpic -shared dog.cpp -o dog
g++ -g -Wall -fpic -shared cat.cpp -o cat
g++ -o foo main.o dog cat -ldl
bash-2.05$ ./foo
Creating cat...
Cat is eating
Destroying cat
Creating dog...
dog is eating
destroying dog
bash-2.05$