#define pr(a, b...) printf(a, b) /* (*A) */ #define pr(a, b...) printf(a, ##b) /* (*B) */ pr("Hello\n"); /* (*1) */ pr("Hello %s\n", "World"); /* (*2) */ pr("Hello %s %s\n", "My", "World"); /* (*3) */


GCC(C89)에서 사용할 수 있는 위의 두가지 macro는 언뜻 같아 보이지만 차이가 존재한다.
(*A) (*B) 모두 (*2)와 (*3) 같은 경우에서 정상적으로 잘 동작한다.
그런데, (*1)의 경우를 보면, 1은 macro에서 정의하고 있는 두번째 arguement가 없는 경우다. 즉 macro argument 'b'가 없다.
따라서 preprocessing결과를 생각해보면, (*A) (*B) 모두

printf(a, )

가 되어서 compile error가 나야 할 것 같은데, (*A)와 같이 정의하면, compile error가 발생하고, (*B)와 같이 정의하면 정상동작한다.
GCC spec.을 살펴보지 않았으나, 주의할 필요가 있어서 적어 둔다. (재미있지 않은가?)

This is example for using function pointer in C. (basically, C++ case can be easily inferred from C's one)

Syntax related with function pointer type is quite different from other normal(?) one.
So, here is summary.

* type definition
typedef void (*<type name>) (int, void*);

* variable definition
void* (*<var name>) (int, void*) = NULL;
=> define function pointer variable that's type is "void*(*)(int, void*)" and initialize it into 'NULL'.

* type casting.
fpointer1 = (void*(*)(int, void*)) fpointer2;
=> cast function pointer 'fpointer2' into "void(*)(int,void*)" type.

* function that returns function pointer - float(*)(float, float).
=> float (*get_ptr(char c))(float, float); <- parameter is "char c", return type is "float(*)(float, float)"

* function - returns function pointer - pointer variable .
=> void(*(*var_name)(char c)) (void*); <- return function pointer type is "void(*)(void*)", parameter is "char c" and variable name is 'var_name'

* declare function pointer variable whose return type is function pointer.
=> int(* (*get_func)(int))(int, int); <- parameter of function is "int" And it returns function pointer "int(*)(int,int)"

* typecasting to function pointer whose return type is function pointer.
=> int(*(*)(int))(int, int); <- pointed function's return type is "int(*)(int,int)&quot;.

* array of function pointer.
=> static int(*array_name[])(float) = { ... }; <- "int(*)(float)" typed function pointer array.

* pointer of function pointer
=> void*(**<var name>)(int, void*); <- <var name> is pointer of function pointer. it's type is "void*(**)(int, void*)"

Let's narrow the subject down to the 'C' case.

In case of implementing reusable library, sometimes we want to add some hidden information at the allocated memory used in the library. To achieve this, library may support special paired-interface. For example

    void* mylib_malloc(unsigned int size);
    void mylib_free(void* p);

To simplify discussion, let's assume that we want add just hidden 'int' value.
We can easily think two options.
- add hidden value to the end of allocated memory. -- (*1)
- add to the start of it. -- (*2)

But we cannot use (*1), because, in general, we cannot know size of memory that is allocated. Therefore there is no general way to access hidden value by address.
So, (*2) is only way we can use. Actually, this is general way to implement standard 'malloc' function, too.

then, memory block structure will be like this.

            returned value
             of 'mylib_malloc'
                 |
                 v
    +------------+-------------------
    | hidden-int | user-allocated
    +------------+-------------------

We should keep in mind these.
- Size of hidden value depends on data-align-constraint. That is, memory address of user-allocated-space should obey data-align-constraint. For example, in case of 32bit ARM, 4 byte data align is required. So, size of hidden value should be multiple of 4 to get 4-byte-aligned-address for user space.
- 'mylib_alloc/mylib_free' should make a pair. (Standard-free-function should not be used - it raises error.)

"Signal and Slot" concept in QT is very impressive. So, I mimicked it. Here is my mimicked version sample.
(I know... This is humble... But, it has been useful for me..)

typedef struct {
  void*   obj;
  void*   func;
}___yZw_yzW_FiT___;
...
typedef void (*___yZw_yzW_FuncT1___) (void*, void*);
...
#define SLOT1_TYPE  ___yZw_yzW_FuncT1___
...
#define DEF_SLOT1(cLASS, sLOT, tYPE1) \
void  sLOT (tYPE1); \
static void ___##sLOT##_yZw_s___( void* ___yZw_t___,  \
                                  void* ___yZw_d1___) {\
  ASSERT( ___yZw_t___ && ___yZw_d1___ ); \
  tYPE1 a1;\
  memcpy(&a1, ___yZw_d1___, sizeof(tYPE1)); \
  ((cLASS*)___yZw_t___)->sLOT(a1); \
}
...
#define DEF_SIGNAL1(cLASS, sIGNAL, tYPE1) \
CArr<___yZw_yzW_FiT___>    ___yZw_##sIGNAL##_a___;\
void sIGNAL (tYPE1 ___yZw_a1) const { \
  for(int i=0; i<___yZw_##sIGNAL##_a___.Size(); i++) {\
    (*((___yZw_yzW_FuncT1___)(___yZw_##sIGNAL##_a___[i].func)))(___yZw_##sIGNAL##_a___[i].obj, \
                                                                (void*)&___yZw_a1 ); \
  }\
}
...
// argument [sIG_CLASS] is reserved for future use.
#define CONNECT(sIG_CLASS, sIG_OBJ, sIGNAL, sLOT_CLASS, sLOT_OBJ, sLOT) \
do{\
  int __yZw_i__; \
  for(__yZw_i__=0; __yZw_i__<(sIG_OBJ)->___yZw_##sIGNAL##_a___.Size(); __yZw_i__++){ \
    if( ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].obj == (void*)(sLOT_OBJ)) && \
      ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].func == (void*)&sLOT_CLASS::___##sLOT##_yZw_s___) ){ break; } \
  } \
\
  if(__yZw_i__ >= (sIG_OBJ)->___yZw_##sIGNAL##_a___.Size() ) \
  { /* Not in the list */ \
    ___yZw_yzW_FiT___   _info; \
    _info.obj = (void*)(sLOT_OBJ); \
    _info.func = (void*)(&sLOT_CLASS::___##sLOT##_yZw_s___); \
    ((sIG_OBJ)->___yZw_##sIGNAL##_a___).Append(_info); \
  } \
  else {;/* do nothing - duplicated slot is not allowed */ } \
}while(0);

// argument [sIG_CLASS] is reserved for future use.
#define DISCONNECT(sIG_CLASS, sIG_OBJ, sIGNAL, sLOT_CLASS, sLOT_OBJ, sLOT) \
do{ \
  int __yZw_i__; \
  for(__yZw_i__=0; __yZw_i__<(sIG_OBJ)->___yZw_##sIGNAL##_a___.Size(); __yZw_i__++){ \
    if( ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].obj == (sLOT_OBJ)) && \
      ((sIG_OBJ)->___yZw_##sIGNAL##_a___[__yZw_i__].func == &sLOT_CLASS::___##sLOT##_yZw_s___) ){ break; } \
  } \
  if( __yZw_i__<(sIG_OBJ)->___yZw_##sIGNAL##_a___.Size() ) { ((sIG_OBJ)->___yZw_##sIGNAL##_a___).Remove(__yZw_i__); } \
}while(0);
...

Here is example code to use this.

// Class definition
class CObj1
{
public:
  CObj1(void){
    _a = 0; _b = 0;
  }

  // virtual DEF_SLOT overriding.
  void  Name(char*);

  // DEF_SLOT functions.
  DEF_SLOT1(CObj1, Set_A,
            int);
  DEF_SLOT1(CObj1, Set_B,
            int);

  // DEF_SIGNAL functions.
  DEF_SIGNAL1(CObj1, Put_A,
              int);
  DEF_SIGNAL1(CObj1, Put_B,
              int);

private:
  int   _a;
  int   _b;
};
...

// Main code

int main() {
  CObj1 o1_1;
  CObj1 o1_2;

  o1_1.Set_A(1);
  o1_1.Set_B(2);

  CONNECT(CObj1, &o1_1, Put_A,    // Signal
          CObj1, &o1_2, Set_B );  // Slot
  CONNECT(CObj1, &o1_1, Put_A,    // Signal
          CObj1, &o1_2, Set_A);   // Slot
  ...
}

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]
 

In MSVC 2008 Express, following code raises error.

template  class A
{
    int   Func(int (*f)(const T, const T));
}

static int TagF(const Tag* a, const Tag* b){...}

int main()
{
    A a;
    a.Func(TagF); // error : type of 1st parameter is mismatch...bla-bla...
}

Intersetingly, following is well-compiled in MSVC 2008 Express.

// Definition of Class A is same with above.

typedef Tag*  PTag;
static int TagF(const PTag a, const PTag b) {...}

int main()
{
    A a;
    a.Func(TagF);
}

Is it a bug of MSVC? or Do I miss something????

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

In terms of function's return value, I classify three types.

type 1 : Function may fail with quite high possibility. 
return value tells details about success or fail reason.
Ex.)

return_value == 0 : general success.
return_value > 0 : success with additional information [value represents additional information]
return_value < 0 : fails [value represents error code]

int open_service(...) {...}

type 2 : Function may fail with very low possibility.
return value tells the result or "success or fail".
Ex.)

return_value == NULL : fails.
otherwise : return_value is result.

Item* List::next() {...}
FILE* fopen(...) {...}

In this case, we may need additional function like "get_last_error()" - like MSVC's API.

type 3 : Function never fails.
return value is result, 'void' or instance itself to easy-next-use.
Ex.)

int get_id() {...}
void set_color(int color) {...}
Paint* Paint::set_color(int color) {...; return this;}

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]
 

There two type of using 'new' operator.

new Type(param);
new Type[size];

'new' is operator. So, parameter is followed by. Therefore, we should take care of using '()'.
Here is test result on MSVC7.

(*1)

new (Typename(param)); // -- compile error
new (Typename)(param); // -- OK
new (Typename[size]);  // -- OK
new (Typename[size])(); // -- OK
new (Typename)[size];  // -- compile error
new (Typename[size])(param); // -- compile error

Interestingly, 'Type[size]' itself seems to be regarded as type. That is,

int a[10]; // 'sizeof(a)' works

similarly

Type[10]; // 'sizeof(Type[10])' works.

Considering 'Type[size]' as a kind of type may not be true. But, I think it's similar with the case of pointer - pointer is type of not. Only to understand things easily, personally, I will regard it as type.

Let's see (*1) from "C operator"'s point of view - 'new' also a kind of operator.

operator new ( Typename(param) ); //  (*a)
operator new (Typename) (param); // (*b)
operator new (Typename[size]); // (*c)
operator new (Typename[size])(); // (*d)
operator new (Typename)[size]; // (*e)
operator new (Typename[size])(param) // (*f)

(*a) : 'Type(param)' is syntax for create instance. Not type name.
(*b) : '(param)' is parameter of instance created by '(operator new(Typename))'
(*c) : 'Typename[size]' is just a type. So, no problem!
(*d) : same with above. It's like "calling default constructor of newly created instance".
(*e) : This is a form like "instance[size]" - (operator new (Typename))[size]. Array should be declard with type (Not instance).
(*f) : (operator new (Typename[size])) (param). This is like "calling constructor whose parameter is '(param)'". But, ISO C++ forbids initialization in array new.

Hmmm..... Interesting.....

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

Constructor doesn't give return value!.
Yes, we can pass pointer as parameter and get value. But it's not good shape.
So, we shouldn't write code that may prevent class from instanciation in constructor. And, we may need additional interface for initializing that can give return value - like 'int init()'.
[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

Using 'const' wherever possible is very very important. It can help to avoid mistakes and increase readability very much.
Personally, I even regard ability of using 'const' in code, as one of most important evidence to evaluate programmer's quality.

==== const member function ====

class A {
    void funcB(void) const;
    void funcA(int* pa) const;
    int* funcC(void) const;
    int a
}

const member function(henceforth const member) cannot change class member variable. And, const member can call only const member. But, const member can call any C function.
By the way, there is no constraints about parameter in const member. So, following is possible.

Hack 1
  void A::funcB(void) const { funcA(&a); }
  void A::funcA(int* pa) const { *pa = 9; }

Hack 2
  void A::funcB(void) const ( int* p = funcC(); *p = 8; }
  int* A::funcC(void) const {return &a;}

Even if, funcB changes value of member variable, it compiles well. This is one of week points of const member.

==== const & pointer ====

const int* p;   // *1 - (*p) is const
int const *p;   // *2 - (*p) is const
int* const p;   // *3 - (p) is const

case *1, *2;

"*p = 4" is error, but, "p = &b" is OK.
So, we can hack this like "*((int*)p) = 9;".

case *3

contrary to upper case. So, we should initialize pointer before using it.

==== const & multiple pointer ====

const int** p;   // *1
int const **p;   // *2
int* const *p;   // *3
int** const p:   // *4

int*       pb;
const int* cpb
int b;

case *1, *2.

// p = &pb; // error - "cannot convert from 'int **' to 'const int **"
p = &cpb; // OK.
*p = &b;
// **p = 8; // error - "you cannot assign to a variable that is const"
*((int*)*p) = 8 // OK. Hack!

case *3

p = &pb; // OK
*p = &b; // error - "you cannot assign to a variable that is const"

case *4

p = &pb; // error - "you cannot assign to a variable that is const". p should be initialized before used.

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

What is variable in C? Let's think about this deeply.

The variable consists of type and name.
Then, what is 'type'? and what is name?
'type' is "What the memory value means"; Way of interpreting memory.
'name' is alias of memory address.

Can you understand?
We can know that 4-byte-memory-value from address '&a' represents 'signed integer value' from C code "int a;".

Following example(ARM assembly and C codes) shows this explicitly.

// asm.s
EXPORT my_symbol
CODE32
my_symbol
DCD 0x73d71034

------------

// symbol.c
extern char* my_symbol;
int data = (int)my_symbol;

+ Recent posts