This question is about gcc constructor, compile & link is right, but it NOT run.
There is a.c:
UTEST_BEGIN()
UID(a_test)
{
printf("a test");
return true;
}
UTEST_END(a)
b.c is simlar:
UTEST_BEGIN()
UID(b_test)
{
printf("b test");
return true;
}
UTEST_END(b)
The code object is using UID() link some test functions. My first version add UTEST_BEGIN() UTEST_END() to enclose UID(), at last I realize UTEST_BGIN() UTEST_END() isn't necessary, when I change them get unpredicated result.
when I change the definition of UTEST_BEGIN(), UID(), UTEST_END(), I got different result.
The basic idea come from can-i-auto-collect-a-list-of-function-by-c-macro!
Test 1:
#define UTEST_BEGIN() \
static const bool __m_en = true; \
static struct __uti *__m_uti_head = NULL;
bool utest_item_list_add_global(struct __uti *uti);
#define UID(f) \
static bool __uti_##f(void); \
__attribute__((constructor)) \
static void uti_construct_##f(void) \
{ \
printf("%s\n", #f); \
static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f }; \
utest_item_list_add_global(&__m_uti_##f); \
} \
static bool __uti_##f(void)
bool unit_test_item_pump_do(int file_id, bool (*f)(void), const char *f_name);
#define UTEST_END(file_name) \
bool unit_test_##file_name(void) \
{ \
if (!__m_en) \
return true; \
struct __uti *cur; \
for(cur = __m_uti_head; cur; cur = cur->next) { \
unit_test_set_run_last_line(__LINE__); \
if (!unit_test_item_pump_do(this_file_id, cur->f, cur->f_name)) \
return false; \
} \
return true; \
}
I got right result. I can call __uti_a_test() and __uti_b_test() through a link. In fact, the __uti_xxx() link is NOT realated with __m_uti_head, so I want to remove UTEST_BEGIN() & UTEST_END().
run gcc -E a.c, the macro extend as:
static const bool __m_en = 1;
static struct __uti *__m_uti_head = ((void *)0);
static bool __uti_a_test(void);
__attribute__((constructor))
static void uti_construct_a_test(void)
{
static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" };
utest_item_list_add_global(&__m_uti_a_test);
}
static bool __uti_a_test(void)
{
printf("a test");
return 1;
}
bool unit_test_a(void)
{
if (!__m_en)
return 1;
struct __uti *cur;
for(cur = __m_uti_head; cur; cur = cur->next) {
unit_test_set_run_last_line(19);
if (!unit_test_item_pump_do(file_id_a, cur->f, cur->f_name))
return 0;
}
return 1;
}
Test 2:
#define UTEST_BEGIN()
bool utest_item_list_add_global(struct __uti *uti);
#define UID(f) \
static bool __uti_##f(void); \
__attribute__((constructor)) \
static void uti_construct_##f(void) \
{ \
printf("%s\n", #f); \
static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f }; \
utest_item_list_add_global(&__m_uti_##f); \
} \
static bool __uti_##f(void)
#define UTEST_END(file_name)
The definition of UID() is same as Test 1. I keep UTEST_BEGIN() & UTEST_END() as blank. Compile & Link is right, But uti_construct_a_test() & uti_construct_b_test() NOT execute.
run gcc -E a.c, the macro extend as:
static bool __uti_a_test(void);
__attribute__((constructor))
static void uti_construct_a_test(void)
{
static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" };
utest_item_list_add_global(&__m_uti_a_test);
}
static bool __uti_a_test(void)
{
printf("a test");
return 1;
}
The utest_item_list_add_global() is exist in other .c file, the function add a node into a link:
static struct __uti *m_uti_head = NULL;
bool utest_item_list_add_global(struct __uti *uti)
{
if (NULL == m_uti_head) {
m_uti_head = uti;
return true;
}
struct __uti *tail = m_uti_head;
while (NULL != tail->next)
tail = tail->next;
tail->next = uti;
return true;
}
The expanded macor is seem as right. I think the problem is in link stage, am I right?
I found gcc attribute((constructor)) have below fact:
cons.c is a file which contain constructor function.
If only constructor is exist in cons.c file, compile it as static library, then link it with main(), the constructor will be ignore.
If any function which is called in main.c is exist in cons.c, compile cons.c as static library, then link it with main(), the constructor will be called before main.
If use "gcc main.c cons.c", constructor will be called before main.
cons.c:
#include <stdio.h>
static void __attribute__((constructor)) construct_fun(void)
{
printf("this is a constructor\n");
}
void cons(void)
{
printf("this is cons\n");
}
test 1:
main.c:
#include <stdio.h>
int main(void)
{
printf("this is main\n");
}
compile by:
gcc -c cons.c
ar cqs libcon.a cons.o
gcc main.c libcon.a
output is:
this is main
test 2:
main.c:
#include <stdio.h>
extern void cons(void);
int main(void)
{
cons();
printf("this is main\n");
}
compile by:
gcc -c cons.c
ar cqs libcon.a cons.o
gcc main.c libcon.a
output:
this is a constructor
this is cons
this is main
test 3:
main.c
#include <stdio.h>
int main(void)
{
printf("this is main\n");
}
compile by:
gcc main.c cons.c
output:
this is a constructor
this is main
run "gcc -v", output:
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-redhat-linux/4.7.2/lto-wrapper
Target: i686-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --disable-build-with-cxx --disable-build-poststage1-with-cxx --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linux
Thread model: posix
gcc version 4.7.2 20121109 (Red Hat 4.7.2-8) (GCC)
My question is:
Only constructor is exist in a .c file, compile it as static library, Why gcc ignore the construct? How to avoid it?
Related
I am implementing my own generic Threadpool algorithm in C, using the fibonacci sequence for test purposes, and for the last few days I have been stuck with a problem that completely manages to baffle me.
When executing the program it will work until at some point it suddenly stops for no reason that is readily apparent for me.
The one thing I noticed is that the execution stops after some small amount of time, as it stops earlier in the execution if print commands or sleep commands are added to it.
EDIT: Missed this part, I already tested for Deadlocks and there are none, it seems to just not push any new things onto the stack at some point, leading to all threads just trying to pull from the stack, recognising it's empty and jumping back up just to repeat that process ad-infinitum.
Here is the code:
threadpool.h
#ifndef THREADPOOL_H_INCLUDED
#define THREADPOOL_H_INCLUDED
#include <stddef.h>
#include <stdbool.h>
typedef void (*ThreadTask_f)(void*);
typedef struct Future {
ThreadTask_f fn; //Pointer to the to be executed function
bool fulfilled;
} Future;
extern int tpInit(size_t size);
extern void tpRelease(void);
extern void tpAsync(Future *future);
extern void tpAwait(Future *future);
/* creates an abstraction for easy interaction of functions with the threadpool
* TYPE: type that the function returns
* NAME: name of the function to be parralelised
* ARG: type of the argument of the function given
*/
#define TASK(TYPE, NAME, ARG) \
TYPE NAME(ARG); \
\
typedef struct { \
Future fut; \
ARG arg; \
TYPE res; \
} NAME ## _fut; \
\
static void NAME ## Thunk(void *args) { \
NAME ## _fut *data = args; \
data->res = NAME(data->arg); \
} \
static inline NAME ## _fut NAME ## Future(ARG arg) { \
return (NAME ## _fut) { \
.fut = { .fn = &NAME ## Thunk, .fulfilled = false }, \
.arg = arg \
}; \
} \
static inline NAME ## _fut* NAME ## Async(NAME ## _fut *future) { \
tpAsync(&future->fut); \
return future; \
} \
static inline TYPE NAME ## Await(NAME ## _fut *future) { \
tpAwait(&future->fut); \
return future->res; \
}
#endif
threadpool.c
#include "threadpool.h"
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <time.h>
#define THREADSTACKSIZE 8388608
#define INITSTACKSIZE 1024 //initial value for how many Tasks can be in the taskstack
#define STACKMEMMULT 2 //if the TaskStack is full, multiply by this
typedef struct TaskStack {
Future **start;
size_t size;
long current;
} TaskStack;
typedef struct ThreadPool {
size_t size;
pthread_t *threads;
TaskStack *stack;
} ThreadPool;
static pthread_mutex_t stackAccess;
static ThreadPool *tp;
void nsleep(unsigned long nano) {
struct timespec delay = {
.tv_sec = 0,
.tv_nsec = nano
};
nanosleep(&delay, NULL);
}
static void push(Future *future){
pthread_mutex_lock(&stackAccess);
if(tp->stack->current++==tp->stack->size){
tp->stack->size*=2;
tp->stack->start=realloc(tp->stack->start, tp->stack->size);
}
tp->stack->start[tp->stack->current]=future;
pthread_mutex_unlock(&stackAccess);
}
static Future *pull(){
Future *retVal=NULL;
PULLBEGIN:
pthread_mutex_lock(&stackAccess);
if(tp->stack->current==-1){ //if there is nothing on the stack test if there is a cancel attempt and yield the scheduler to a thread that might add tasks.
pthread_mutex_unlock(&stackAccess);
pthread_testcancel();
sched_yield();
goto PULLBEGIN;
}
retVal=tp->stack->start[tp->stack->current];
tp->stack->current--;
pthread_mutex_unlock(&stackAccess);
return retVal;
}
static void *workerThread(void *args){
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
Future *fut;
while(true){
fut=pull();
fut->fn(fut);
fut->fulfilled=true;
pthread_testcancel();
}
return NULL;
}
int tpInit(size_t size) {
int err;
tp=NULL;
accessStack=0;
pushExisting=0;
pthread_mutex_init(&stackAccess, NULL);
tp=malloc(sizeof(ThreadPool));
if(tp==NULL){
err=0;
goto ERRHANDLINIT;
}
tp->size=0;
tp->stack=malloc(sizeof(TaskStack));
if(tp->stack==NULL){
err=1;
goto ERRHANDLINIT;
}
tp->threads=malloc(sizeof(pthread_t)*size);
if(tp->threads==NULL){
err=2;
goto ERRHANDLINIT;
}
tp->stack->start=malloc(sizeof(Future *)*INITSTACKSIZE);
if(tp->stack->start==NULL){
err=3;
goto ERRHANDLINIT;
}
tp->stack->current=-1;
tp->stack->size=INITSTACKSIZE;
pthread_attr_t attributes;
if(pthread_attr_init(&attributes)!=0){
err=4;
goto ERRHANDLINIT;
}
if(pthread_attr_setstacksize(&attributes, THREADSTACKSIZE)!=0){
err=5;
goto ERRHANDLINIT;
}
if(pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE)!=0){
err=6;
goto ERRHANDLINIT;
}
for(int i=0; i<size;i++){
if(pthread_create(&(tp->threads[i]), &attributes, workerThread,NULL)!=0){
err=20+i;
goto ERRHANDLINIT;
}
}
return 0;
ERRHANDLINIT:
perror("Problem while initiating the threadpool with the following errcode: ");
fprintf(stderr,"%i\n", err);
return -1;
}
void tpRelease(void) {
for(int i=0; i<tp->size; i++){
pthread_cancel(tp->threads[i]);
pthread_join(tp->threads[i], NULL);
}
free(tp->stack->start);
free(tp->stack);
free(tp->threads);
free(tp);
}
void tpAsync(Future *future) {
future->fulfilled=false;
push(future);
return;
}
void tpAwait(Future *future) {
while(!future->fulfilled){
Future *workFut=pull();
workFut->fn(workFut);
workFut->fulfilled=true;
}
}
main.c
#include "threadpool.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
static TASK(long, fib, long);
long fib(long n) {
if (n <= 1){
return n;
}
fib_fut *a = fibAsync((fib_fut[]) { fibFuture(n - 1) });
fib_fut *b = fibAsync((fib_fut[]) { fibFuture(n - 2) });
return fibAwait(a) + fibAwait(b);
}
int main() {
if (tpInit(8) != 0)
perror("Thread Pool initialization failed"), exit(-1);
atexit(&tpRelease);
for (long i = 0; i <= 100; ++i)
printf("fib(%2li) = %li\n", i, fib(i));
return 0;
}
Makefile
#!/usr/bin/make
.SUFFIXES:
.PHONY: all run pack clean
SRC = $(wildcard *.c)
OBJ = $(SRC:%.c=%.o)
TAR = threadpool
CFLAGS = -std=gnu11 -c -g -Os -Wall -MMD -MP
LFLAGS = -pthread
DEP = $(OBJ:%.o=%.d)
-include $(DEP)
%.o: %.c
$(CC) $(CFLAGS) $< -o $#
$(TAR): $(filter-out quicksort.o,$(OBJ))
$(CC) $(LFLAGS) -o $# $^
all: $(TAR)
run: all
./$(TAR)
clean:
$(RM) $(RMFILES) $(OBJ) $(TAR) bench $(DEP) $(PCK)
I really hope you have some idea what is happening. Thank you in advance.
So I figured it out, with the gracious help of Craig Estey and Amit (which you can see in the comments under the original post).
So in the end it was a Deadlock, because, as you can still see in the original post which I will not modify so anyone interested has a chance to gaze upon my folly.
This happened because at one point there will be 6 threads waiting to pull, the stack is empty, and the two remaining threads is one going into await, and the other just having fulfilled it's given function, which is one that didn't call another recursively (in our example one with fib(0) or fib(1)). Now that that is finished the thread, let's call it thread 7, going into fib_await() will check if the value that it is waiting for is fulfilled, which at this point it isn't yet, thus it checks if there are any other in the stack. As there are none, it is stuck in wait.
Now the other thread, thread 8, the one that just fulfilled it's given function marks it's future as fulfilled and tries to pull another future. As it is empty, it too will stay in pull.
Now all threads are stuck in pull and none can progress as the one that is waiting on another would first have to leave pull().
My only modifications came for pull(), push(), tpAwait(), tpInit(), and workerThread() as I also implemented a very simple ticket lock.
𝛿 threadpool.c
static void ticketLockInit(){
atomic_init(&nowServing, 0);
atomic_init(&nextTicket, 0);
}
static inline void ticketLockAcquire(){
atomic_long myTicket=atomic_fetch_add(&nextTicket,1);
while(myTicket!=nowServing){
nsleep(1);
}
}
static inline void ticketLockRelease(){
++nowServing;
}
static void push(Future *future){
ticketLockAcquire();
if(++tp->stack->current==tp->stack->size){
fprintf(stderr, "MemRealloc\n");
tp->stack->size=tp->stack->size*2;
tp->stack->start=realloc(tp->stack->start, tp->stack->size);
}
tp->stack->start[tp->stack->current]=future;
ticketLockRelease();
}
static Future *pull(){
Future *retVal=NULL;
ticketLockAcquire();
if(tp->stack->current>-1){ //if there is nothing on the stack test if there is a cancel attempt and yield the scheduler to a thread that might add tasks.
retVal=tp->stack->start[tp->stack->current];
tp->stack->current--;
}
ticketLockRelease();
return retVal;
}
static void *workerThread(void *args){
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
Future *fut;
while(true){
if((fut=pull())!=NULL){
fut->fn(fut);
fut->fulfilled=true;
pthread_testcancel();
}
}
return NULL;
}
void tpAwait(Future *future) {
while(!future->fulfilled){
Future *workFut;
if((workFut=pull())!=NULL){
workFut->fn(workFut);
workFut->fulfilled=true;
pthread_testcancel();
}
}
}
int tpInit(size_t size) {
int err;
tp=NULL;
accessStack=0;
pushExisting=0;
pthread_mutex_init(&stackAccess, NULL);
ticketLockInit();
tp=malloc(sizeof(ThreadPool));
if(tp==NULL){
err=0;
goto ERRHANDLINIT;
}
tp->size=0;
tp->stack=malloc(sizeof(TaskStack));
if(tp->stack==NULL){
err=1;
goto ERRHANDLINIT;
}
tp->threads=malloc(sizeof(pthread_t)*size);
if(tp->threads==NULL){
err=2;
goto ERRHANDLINIT;
}
tp->stack->start=malloc(sizeof(Future *)*INITSTACKSIZE);
if(tp->stack->start==NULL){
err=3;
goto ERRHANDLINIT;
}
tp->stack->current=-1;
tp->stack->size=INITSTACKSIZE;
pthread_attr_t attributes;
if(pthread_attr_init(&attributes)!=0){
err=4;
goto ERRHANDLINIT;
}
if(pthread_attr_setstacksize(&attributes, THREADSTACKSIZE)!=0){
err=5;
goto ERRHANDLINIT;
}
if(pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE)!=0){
err=6;
goto ERRHANDLINIT;
}
for(int i=0; i<size;i++){
if(pthread_create(&(tp->threads[i]), &attributes, workerThread,NULL)!=0){
err=20+i;
goto ERRHANDLINIT;
}
}
return 0;
ERRHANDLINIT:
perror("Problem while initiating the threadpool with the following errcode: ");
fprintf(stderr,"%i\n", err);
return -1;
}
I am browsing through telegram-cli source code to make some changes. However, I am stuck at a syntax that I am not able to understand.
#define user_cmp(a,b) (tgl_get_peer_id ((a)->id) - tgl_get_peer_id ((b)->id))
DEFINE_TREE(user, struct tgl_user *,user_cmp,0)
static void notify_status (struct tgl_user *U, void *ex) {
struct tgl_state *TLS = ex;
if (TLS->callback.user_status_update) {
TLS->callback.user_status_update (TLS, U);
}
}
static void status_notify (struct tgl_state *TLS, void *arg) {
tree_act_ex_user (TLS->online_updates, notify_status, TLS);
tree_clear_user (TLS->online_updates);
TLS->online_updates = NULL;
TLS->timer_methods->free (TLS->online_updates_timer);
TLS->online_updates_timer = NULL;
}
The tree_act_ex_user function is not defined anywhere and IDE is navigating to second line i.e. DEFINE_TREE(user, struct tgl_user *,user_cmp,0). Please explain or point to a reference what construct is this?
what construct is this?
A macro expansion that defines a function. A reduced example:
#define DEFINE_TREE(NAME, some_arg, some_arg2, yet_another_arg) \
static void tree_act_ex_##NAME() { \
do_something(); \
}
DEFINE_TREE(some_string, arg1, arg2, arg3)
// expands to:
// static void tree_act_ex_some_string() { do_something(); }
int main() {
tree_act_ex_some_string();
}
I want to generate multiple similar functions replacing just one word across the function.
As an example, for each of the below:
OBJECT = customer
OBJECT = account
use the function template:
void add_OBJECT_to_array(void* item_ptr, int pos)
{
mtx_lock(&OBJECT_array_mtx);
OBJECT_array[pos] = *(OBJECT_t*)item_ptr;
mtx_unlock(&OBJECT_array_mtx);
return;
}
So that I can call
add_order_to_array(ord, 1);
add_customer_to_array(cust, 1);
Is this possible?
Totally possible. You just need to know about the preprocessor concatenation operator ##. The following code will generate two functions add_order_to_array and add_customer_to_array.
#define GENERATE_FUNC(OBJECT) \
void add_ ## OBJECT ## _to_array(void* item_ptr, int pos)\
{ \
mtx_lock(&OBJECT ## _array_mtx); \
OBJECT ## _array[pos] = *(OBJECT ## _t*)item_ptr; \
mtx_unlock(&OBJECT ## _array_mtx); \
return; \
}
GENERATE_FUNC(order)
GENERATE_FUNC(customer)
The preprocessor output will be (unfortunately it does not respect formatting):
void add_order_to_array(void* item_ptr, int pos) { mtx_lock(&order_array_mtx); order_array[pos] = *(order_t*)item_ptr; mtx_unlock(&order_array_mtx); return; }
void add_customer_to_array(void* item_ptr, int pos) { mtx_lock(&customer_array_mtx); customer_array[pos] = *(customer_t*)item_ptr; mtx_unlock(&customer_array_mtx); return; }
Yes it's possible:
#define DECLARE_ADD_FUNCTION(__obj) \
void add_##__obj##_to_array(void* item_ptr, int pos) \
{ \
mtx_lock(&__obj##_array_mtx); \
__obj##_array[pos] = *(__obj##_t*)item_ptr; \
mtx_unlock(&__obj##_array_mtx); \
return; \
}
DECLARE_ADD_FUNCTION(customer)
DECLARE_ADD_FUNCTION(account)
When you look at the output of the preprocessor you get:
gcc -E foo.c
void add_customer_to_array(void* item_ptr, int pos) { mtx_lock(&customer_array_mtx); customer_array[pos] = *(customer_t*)item_ptr; mtx_unlock(&customer_array_mtx); return; }
void add_account_to_array(void* item_ptr, int pos) { mtx_lock(&account_array_mtx); account_array[pos] = *(account_t*)item_ptr; mtx_unlock(&account_array_mtx); return; }
You can even ensure that the pointer type is the correct type by changing the function prototype to add_##__obj##_to_array(__obj##_t *, int pos)
I would like to run the function pointed by my struct with auto-filling functionality.
This is the part that I'm working on:
#include <stdio.h>
#include <stdlib.h>
struct Fra { //Fraction
int n; //Numerator
int d; //Denominator
void (*p)(struct Fra*);
void (*sD)(int, struct Fra*);
void (*sN)(int, struct Fra*);
};
void print(struct Fra*);
void setDenom(int, struct Fra*);
void setNum(int, struct Fra*);
int main() {
struct Fra* fraA = 0;
fraA = (struct Fra*) malloc(sizeof(struct Fra));
fraA->sN = setNum;
fraA->sN(2, fraA);
fraA->sD = setDenom;
fraA->sD(3, fraA);
fraA->p = print;
fraA->p(fraA);
return 0;
}
And this is what I've been trying to achieve
From:
fraA->sN(2, fraA);
fraA->sD(3, fraA);
fraA->p(fraA);
To:
fraA->sN(2);
fraA->sD(3);
fraA->p();
After spending some time on trial-error, I've arrived to the conclusion that I need assistance on this. I've tried browsing, but it seems I don't have the right keyword, so I'm unable to verify whether this question is a double or not.
Thanks for any help.
You could declare some macros to savely always pass the correct reference, that's all you can do:
#define FRA_NEW(this, sN sD, sP) \
{ \
(this) = calloc(sizeof(*(this))) \
if (this) \
{ \
(this)->sN = (sN); \
(this)->sN = (sD); \
(this)->sN = (sP); \
} \
}
#define FR_DELETE(this) \
free(this)
#define FRA_PRINT(this) \
(this)->print(this)
#define FRA_SETNUM(this, num) \
(this)->setNum(this, num)
#define FRA_SETDENOM(this, denom) \
(this)->setDenom(this, denom)
Also I'd propose to have "this" always as first parameter to the "member"-functions.
int main(void)
{
struct Fra * fraA = NULL;
FRA_NEW(fraA, setNum, setDenom, print);
if (NULL == fraA)
{
perror("FRA_NEW() failed");
return 1;
}
FRA_SETNUM(fraA, 2);
FRA_SETDENOM(fraA, 3);
FRA_PRINT(fraA);
FRA_DELETE(fraA);
return 0;
}
The only really useful way I can think of would be to provide functions which do the job:
void call_print(struct Fra* fra)
{
fra->p(fra);
}
void call_setDenom(int val, struct Fra* fra)
{
fra->sD(val, fra);
}
void call_setNum(int val, struct Fra* fra);
{
fra->sN(val, fra);
}
and use these:
call_setNum(2, fraA);
call_setDenom(3, fraA);
call_print(fraA);
I have:
car.cc
#include "car.h"
#include <iostream>
using namespace std;
extern "C" Car* create_object()
{
return new Car;
}
Car::Car() {
this->maxGear = 2;
this->currentGear = 1;
this->speed = 0;
}
void Car::shift(int gear) {
if (gear < 1 || gear > maxGear) {
return;
}
currentGear = gear;
}
void Car::brake() {
speed -= (5 * this->getCurrentGear());
std::cout<<"THE SPEED IS:" <<speed<<std::endl;
}
extern "C" void destroy_object( Car* object )
{
delete object;
}
car.h
#ifndef VEHICLES_CAR_H
#define VEHICLES_CAR_H
// A very simple car class
class Car {
public:
Car();
void shift(int gear);
void accelerate();
void brake();
private:
int maxGear;
int currentGear;
int speed;
};
#endif /* VEHICLES_CAR_H */
test.cc
#include "/home/car.h"
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
int main()
{
/* on Linux, use "./myclass.so" */
void* handle = dlopen("/usr/lib/libCarTest.so", RTLD_LAZY);
int (*result)(int);
if (!handle)
{
}
/*dlsym(handle,"accelerate");
cout<<"IN HERE: "<<endl;
dlsym(handle,"brake");
dlclose(handle);*/
Car* (*create)();
void (*destroy)(Car*);
dlerror();
create = (Car* (*)())dlsym(handle, "create_object");
destroy = (void (*)(Car*))dlsym(handle, "destroy_object");
Car* carr = (Car*)create();
carr->brake();
destroy( carr );
dlclose(handle);
/*
Car carr;
carr.brake();
* compilation g++ test.cpp -o tst /path/libcar.so
*/
return 0;
}
After creating libMyLib.so and install it in /usr/lib i've tried to compile test.cc using: g++ test.cc -o tst -ldl. WHY do i need to include -lMyLib? is there a way to compile the code without libMyLib.so? Secondly why dlsym(handle,"brake") is not working? If i change dlsym (Car* (*).... with dlsym(handle,"brake") i get nothing. why?
Appreciate
WHY do i need to include -lMyLib?
Because you need to link to the Car::brake method.
Secondly why dlsym(handle,"brake") is not working?
Because there is no brake symbol. The method Car::brake has a complicated mangled (implementation-defined) name. You can see this in the output of nm -D.
AFAIK, you can solve it by
making all the methods of Car virtual (they will be called through a pointer, so no linking will be needed)
doing it the old C way, ie. export a free function brake() that would call the Car::brake method from the .so
making all the public methods of Car inline and defining them in the header.
emulating the virtual table approach (as we do it in C)
Combining the last two approaches:
class Car {
public:
void brake() { brake_impl(this); }
private:
void (*brake_impl)(Car*);
void do_brake(); // this would be the actual implementation
Car() : brake_impl([] (Car* c){ c->do_brake(); }) { ... }
};
Of course you could split the implementation and the interface so it's not such a mess.