2015年12月1日 星期二

C closure (block pointer)

其實身為一個 Cer,我一直很羨慕像 JavaScript 這樣的語言,有閉包的功能

最近發現一個 clang 的 extension,可以提供 C 語言閉包功能 -- Blocks
(ref: https://en.wikipedia.org/wiki/Blocks_(C_language_extension))

由於是 extension,在編譯的時候要開 -fblocks -lBlocksRuntime
(libBlocksRuntime)

-fblocks 啟用 block pointer
-lBlocksRuntime 連結 <Block.h> 內的函式

它提供一種新的指標,指向一個 block 的位址,有點像 &&label 一樣紀錄 .text segment 的其中一個指令的 address

block pointer 的 declarator 是 '^',用法就像一般的 pointer 一樣,不過 block pointer 是不能 assign 給 non-function type variable 的

我們先做出一個 function type 'fun_t'

typedef void fun_t(void);

接著宣告一個 function type pointer variable

fun_t ^ptr;

然後再 assign 一個 block 給 ptr

ptr = ^(void) { printf("Hello""\n"); };

這個時候 ptr 就像是一個函式一樣,可以被呼叫

ptr();

output 會是


Hello


而這個 block 如果在函式裡,那麼它就在這個函式的 stack 上
假如在 global,那麼就在 .text 上,它的生命週期等同於其他相同 scope 的 auto variable
所以要是我這麼做

void (^functor(void))(void);

int main(void)
{
  void (^func) = functor();


  func();


  return 0;
}

void (^functor(void))(void)
{
  return ^(void) { /* some code here */ };
}

那麼等同於把一個 stack space 的物件傳出函式,這顯然不是什麼好事情吧...

比較直覺的作法是先把它 assign 給一個 static variable,然後再回傳

void (^functor(void))(void)
{
  static void (^func)(void) = ^(void) { /* some code here */ };

  return func;
}

但是這不也就代表著每個被 functor assign 的 function variables 都共享著同一個 static function literal 嗎?
這麼一來要是這個 function literal 有使用到 functor 裡的 variable 的話,就會造成非預期的結果

void (^functor(void))(void);

int main(void)
{
  void (^func1) = functor();

  void (^func2) = functor();

  func1();

  func2();

  return 0;
}

void (^functor(void))(void)
{
  static int x = 5;

  static void (^func)(void) = ^(void)
  {
    printf("%d\n", x);
    x += x;
  };

  return func;
}

結果竟然會是 5 和 10!!!
這不是我們預期的閉包的行為,每個物件應該要各自獨立才對
有兩個函式,就專門處理這問題

Block_copy 負責把 Closure 的 environment copy 一份出來(到 heap?)
Block_release 負責把記憶體釋放還給系統

#include <stdio.h>
#include <Block.h>

void (^functor(void))(void);

int main(void)
{
  void (^func1) = functor();

  void (^func2) = functor();

  func1();

  func2();

  Block_release(func1);
  Block_release(func2);

  return 0;
}

void (^functor(void))(void)
{
  __block int x = 5;

  return Block_copy(^(void)
  {
    printf("%d\n", x);
    x += x;
  });
}

這樣 fun1 和 fun2 的結果就會各自獨立互不干擾了~
到這裡,閉包的功能就都有了

來個閉包的實作範例好了

#include <stdio.h>
#include <Block.h>

typedef struct dog *dog;

struct dog
{
  void (^bark)(void);
  __attribute__((__unavailable__)) char dummy_padding[8 - (sizeof(void (^)(void)) % 8)];
};

struct dog *Dog(const char *);

int main(void)
{
  dog marry = Dog("Merry");

  dog peter = Dog("Peter");

  marry->bark();
  peter->bark();

  Block_release(merry->bark);
  Block_release(peter->bark);
  free(merry);
  free(peter);     

  return 0;
}

struct dog *Dog(const char *name)
{
  struct dog *_dog = (struct dog *)malloc(sizeof(struct dog));

  _dog->bark = Block_copy(^(void) { printf("%s\n", name); });

  return _dog;
}


藉由閉包的特性,我們可以輕易的做出兩個行為不同的 bark 函式
一個印出 "Merry",一個印出 "Peter"

2015年11月22日 星期日

C 多型

剛剛在翻 tgmath.hsource code 的時候發現,在 llvm 的實作裡有一個 attribute "__overloadable__"(ref: http://clang.llvm.org/doxygen/tgmath_8h_source.html)

tgmath.hC99 標準標頭之一,主要目的是讓我們在呼叫 math.h 裡的函式的時候不需要一直去注意呼叫的函式型態

__overloadable__ 的用法就是放在 functiondefinition 旁邊,像這樣,可以達成真正的多型:

__attribute__((__overloadable__)) int print(char c) { return printf("%c\n", c); }
__attribute__((__overloadable__)) int print(int i) { return printf("%d\n", i); }
__attribute__((__overloadable__)) int print(float f) { return printf("%f\n", f); }

而且不只是 parameter list 可以 overload,連 return type 都可以!可惜的是這只是 clang 自己的 extensiongcc 並沒有提供這個功能 (所以趕快換來 clang 的行列吧) 所以在使用上必須小心

不過竟然只是 clangextension,真的是沒有過癮到......
(ref: https://ohse.de/uwe/articles/gcc-attributes.htmlhttp://clang.llvm.org/docs/AttributeReference.html#overloadable)



那麼再來介紹一個和多型有關的東西好了
C11 引進了一個新的 keyword "_Generic",用它可以做到一點點小多型(ref: https://en.wikipedia.org/wiki/C11_(C_standard_revision))

可以寫一個 macro 來幫忙


#define print(x) _Generic((x), char: print_c, int: print_i, float: print_f)((x))

接著各函式的實作


int print_c(char c) { return printf("%c\n", c); }

int print_i(int i) { return printf("%d\n", i); }
int print_f(float f) { return printf("%f\n", f); }

然後就可以呼叫了


int main(void)

{
  char c = 'x';

  print(c);


  return 0;

}

在編譯的時候要記得給編譯器 -std=c11 的參數,開啟 c11 的支援喔~

root@localhost:~# clang -std=c11 -o generic generic.c
root@localhost:~# ./generic
x
root@localhost:~# 

以上都是 compile-time type determination,純 C 目前好像還無法 runtime type determine,想要這些進階功能,還是乖乖用 C++XD

2015年2月6日 星期五

C 語言 快速求最大公因數

#include <stdio.h>
#include <stdlib.h>

long long euqivalent(long long x, long long y)
{
if(!x || !y) return 0;
while((x %= y) > 1 && (y %= x) > 1);
if(x == 1 || y== 1) { return 1; }
else { return (x > y) ? x : y; }
}

int main(int argc, char *argv[])
{

/* check for args vector here */

printf("%lld", equivalent(atoll(argv[1], atoll[2])));

return 0;
}