2016年4月5日火曜日

自動微分をする

よく、コンピュータの理論を勉強する前に基礎である線形代数を勉強してほうが良いといわれています。

この線形代数なのですが、おじさんの時代は主にベクトルとか逆行列とかをやっていました。まぁゲームとかを作る際に座標の計算とかをしないといけないからなのですが。

しかしこの線形代数、時代とともに旬な領域が変化していまして、最近は二重数とか自動微分とかをイギリスの大学で勉強するようです。

おじさんの時代は二乗すると-1になる数はiしかなかったのに。
二乗すると-1になる数三つに増えてるし、近頃は二乗すると0になる二重数があるらしいです。素粒子みたくフェルミ的とかボソン的なんていうらしいです。
二重数の話はまた後ろに書くとして、まずは自動微分について。

自動微分とはプログラムで関数の値の計算をするとなんとその導関数の値も同時に計算できちゃうアルゴリズムというか理論です。

たとえばx=1でいうと
y=x^2を計算すると1ですが、同時にy'=2*xの答え2も計算できてしまう。

ただそれだけなんです。
手動で計算したほうがはやいじゃん!

と思っていたのですが、よく調べてみると、単純な数式ならば手動で微分すればよいんですが、Σとかがある複雑な式の導関数の計算ができたり、ニュートン法と組み合わせれば、逆関数がわからない複雑な方程式が解けたり、計算が高速にできたりします。

早い話が、条件はありますが方程式が高速に解けるのです。
このため、深層学習のとかでも使われていたりします。

この自動微分の計算ですが、二重数と関係があるようで、二重数の虚部というのか実部でない部分の計算と同じなのです。

つまり二重数で関数を計算すると実部が関数の値で、実部でない部分が関数を微分した値になります。


ほんとかよ!素粒子と深層学習が一緒なのかよ!
ということで、確かめてみました。

自動微分の理論からプログラミング
http://kivantium.hateblo.jp/entry/2016/03/25/010320

二重数の理論からプログラミング
http://qiita.com/skitaoka/items/b7d0b19e13a7c6832773

この二つのライブラリで計算値が一緒になるんですね。
あらーびっくり。




2016年3月30日水曜日

IoT用の最軽量httpdを作ってみた。

以前のブログで、軽量httpdのことを書いたのですが、Mongooseのライブラリは軽量といってもソースコードが300キロバイトくらいあります。

簡単に改良できて、貧弱なIoTデバイスとかにも乗せられるように、もっともっと軽量化できないかなと思い、自分でスクラッチで最軽量のhttpdを書いてみました。

丸一日かけて書いたソースコードがこれです。
シングルスレッドでIPV4とIPV6両対応でHTTPの規格であるRFC2616をちゃんと満たしています。さらに一応どんなOSでも動くように書いたつもりです。
500行、10キロバイトのhttpdです。
BSDライセンスがいいのでBSDライセンスにしてみました。
https://github.com/yomei-o/easyhttpd
オープンソース書いたから、メルカリにでも採用応募してみようかな。

easyhttpd.c
------------------------------------------------------------------------
/*
Copyright (c) 2016, Yomei Otani <yomei.otani@gmai.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif /* _WIN32 */
#if defined(unix) || defined(ANDROID_NDK) || defined(__APPLE__)
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif /* unix */
#if defined(_WIN32) && !defined(__GNUC__)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

#ifdef _MSC_VER
#if _MSC_VER >= 1400
#pragma warning( disable : 4996 )
#pragma warning( disable : 4819 )
#endif
#endif
#if defined(_WIN32) && !defined(__GNUC__)
#pragma comment( lib, "ws2_32.lib" )
#endif

#if defined(unix) || defined(ANDROID_NDK) || defined(__APPLE__)
#define closesocket(s) close(s)
#endif /* unix */

#define MAX_LISTEN 20
#define MAX_FD 20
#define PORT 12345
#define RECV_TIMEOUT 30

#define DIR_WEBSOCKET "websocket"


void smplws_init()
{
#ifdef _WIN32
WSADATA wsaData;
int err;

err = WSAStartup(MAKEWORD(2, 0), &wsaData);
if (err != 0)
{
exit(1);
}
#endif
#if defined(unix) || defined(__APPLE__)
signal(SIGPIPE, SIG_IGN);
#endif
}

void smplws_done()
{
#ifdef _WIN32
WSACleanup();
#endif
}

//
// socket
//
int dualsock_server_create(int port)
{
int err;
struct addrinfo hints;
struct addrinfo* res = NULL;
struct addrinfo* ai;
int sockfd;
char service[16];

sprintf(service, "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
err = getaddrinfo(NULL, service, &hints, &res);
if (err != 0) {
return -1;
}
ai = res;
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)return -1;

#if defined(_WIN32) || defined(__CYGWIN__)
{
int one = 0;
err = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof one);
}
#endif

if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0){
closesocket(sockfd);
return -1;
}
if (listen(sockfd, 20) < 0){
closesocket(sockfd);
return -1;
}
return sockfd;
}


int dualsock_create(const char* hostname, int port)
{
int sockfd;
int err;
struct addrinfo hints;
struct addrinfo* res = NULL;
struct addrinfo* ai;
char service[16];

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV;

sprintf(service, "%d", port);

err = getaddrinfo(hostname, service, &hints, &res);
if (err != 0) {
return -1;
}
for (ai = res; ai; ai = ai->ai_next) {
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0){
freeaddrinfo(res);
return -1;
}
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
closesocket(sockfd);
sockfd = -1;
continue;
}
break;
}
freeaddrinfo(res);
return sockfd;
}

int dualsock_accept(int sockfd)
{
int cs;
struct sockaddr_storage sa;
socklen_t len = sizeof(sa);

cs = accept(sockfd, (struct sockaddr*) &sa, &len);
return cs;
}

int dualsock_select(int sockfd, int sec)
{
fd_set readfds;
struct timeval t;
int ret;
//int tmp;

t.tv_sec = sec;
t.tv_usec = 0;

FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
ret = select(sockfd+1, &readfds, NULL, NULL, &t);
return ret;
}



int dualsock_select_multi(int* fds, int n, int sec)
{
fd_set readfds;
struct timeval t;
int ret;
int i;
int maxfd = 0;
t.tv_sec = sec;
t.tv_usec = 0;

FD_ZERO(&readfds);
for (i = 0; i < n; i++){
if (fds[i] != -1){
if (maxfd < fds[i])maxfd = fds[i];
FD_SET(fds[i], &readfds);
}
}
ret = select(maxfd+1, &readfds, NULL, NULL, &t);
return ret;
}

//
// smplws utils
//


int smplws_readch(int fd)
{
int ret;
unsigned char buf[4];
if (fd == -1)return -1;
ret=recv(fd, buf, 1, 0);
if (ret < 1)return -1;
return buf[0];
}

int smplws_readuntil(int fd,char* buf, int sz, int endch)
{
int ret = 0;
int ch;
//int tmp;

if (fd==-1 || buf == NULL || sz < 1)return -1;

ret = strlen(buf);

while (ret <= sz - 2){
//tmp = dualsock_select(fd,0);
//if (tmp == 0)return 0;
ch = smplws_readch(fd);
if (ch == -1)return -1;
buf[ret] = ch;
buf[ret + 1] = 0;
ret++;
if (ch == endch)break;
}
return ret;
}

int smplws_gettime()
{
return (int)time(NULL);
}

//
// dhild connection
//

enum{
CHILD_STATE_CLOSED = 0,
CHILD_STATE_HTTP_REQUEST = 1,
CHILD_STATE_HTTP_HEADER  = 2,
CHILD_STATE_WEB_SOCKET   = 3,
};

enum{
CHILD_MODE_UNKNOWN = 0,
CHILD_MODE_HTTP = 1,
CHILD_MODE_WEB_SOCKET = 2,
};

struct child_data
{
char buf[256];
char tmp[256];
int state;
int mode;

//http
char content_type[64];
int content_length;
char* body;

//websocket
};


void* smplws_child_init(void)
{
void* ret = NULL;

//printf("child_init()\n");
ret = malloc(sizeof(struct child_data));
if (ret == NULL)return NULL;

memset(ret, 0, sizeof(struct child_data));
((struct child_data*)ret)->state = CHILD_STATE_HTTP_REQUEST;
return ret;

}

int smplws_child_send_error(int fd)
{
int ret;
char* str = "HTTP/1.1 403 Error\r\n"
"Cnnection: close\r\n"
"Content-Length: 0\r\n"
"\r\n";
ret=send(fd, str, strlen(str), 0);
if (ret == -1)return -1;
return ret;
}

int smplws_child_send_websocket_connection(int fd, struct child_data* cd)
{
return 0;
}


int smplws_child_parse_header(int fd, struct child_data* cd)
{
return 0;
}

int smplws_child_send_http_response_parse(int fd, struct child_data* cd)
{
cd->body = malloc(1024);
if (cd->body == NULL)return -1;
cd->body[0] = 0;
strcpy(cd->content_type, "text/html\r\n");
strcpy(cd->body, "<HTML><BODY>It wroks!</BODY></HTML>");
cd->content_length = strlen(cd->body);
return 0;
}

int smplws_child_send_http_response_dealloc(int fd, struct child_data* cd)
{
if (cd->body)free(cd->body);
return 0;
}


int smplws_child_send_http_response_header(int fd, struct child_data* cd)
{
char* str;
int ret;
char tmp[64];

str = "HTTP/1.1 200 OK\r\n"
"Cnnection: close\r\n"
"Pragma: no-cache\r\n"
"Cache-Control: no-cache\r\n";
ret = send(fd, str, strlen(str), 0);
if (ret == -1)return -1;

sprintf(tmp, "Content-Length: %d\r\n\r\n", cd->content_length);
ret = send(fd, tmp, strlen(tmp), 0);
if (ret == -1)return -1;

return 0;
}

int smplws_child_send_http_response_body(int fd, struct child_data* cd)
{
send(fd, cd->body, cd->content_length, 0);
return 0;
}


int smplws_child_data(void* vp, int fd)
{
int ret=0,tmp;
struct child_data* c = (struct child_data*)vp;

//printf("child_data()\n");
switch(c->state){
case CHILD_STATE_HTTP_REQUEST:
ret = smplws_readuntil(fd, c->buf, 256, '\n');
if (ret == -1)return -1;
if (ret == 0)return 0;
//printf(">>%s<<\n", c->buf);
c->tmp[0] = 0;
sscanf(c->buf, "%s", c->tmp);
if (strcmp(c->tmp, "GET") != 0){
smplws_child_send_error(fd);
return -1;
}
c->tmp[0] = 0;
sscanf(c->buf + 3, "%s", c->tmp);
if (strstr(c->tmp, "/" DIR_WEBSOCKET "/") == c->tmp)c->mode = CHILD_MODE_WEB_SOCKET;
else if (strstr(c->tmp, "/") == c->tmp)c->mode = CHILD_MODE_HTTP;
if(c->mode==CHILD_MODE_UNKNOWN){
smplws_child_send_error(fd);
return -1;
}
c->state = CHILD_STATE_HTTP_HEADER;
break;
case CHILD_STATE_HTTP_HEADER:
ret = smplws_readuntil(fd, c->buf, 256, '\n');
if (ret == -1)return -1;
if (ret == 0)return 0;
if (strcmp(c->buf, "\r\n") == 0){
if (c->mode == CHILD_MODE_WEB_SOCKET){
c->state = CHILD_STATE_WEB_SOCKET;
smplws_child_send_websocket_connection(fd, c);
break;
}
if (c->mode == CHILD_MODE_HTTP){
tmp=smplws_child_send_http_response_parse(fd, c);
if(tmp == -1){
smplws_child_send_error(fd);
return -1;
}
smplws_child_send_http_response_header(fd, c);
smplws_child_send_http_response_body(fd, c);
tmp = smplws_child_send_http_response_dealloc(fd, c);
return -1;
}
smplws_child_send_error(fd);
return -1;
}
smplws_child_parse_header(fd, c);
break;
case CHILD_STATE_WEB_SOCKET:
ret = -1;
break;
}

printf(">>%s<<\n", c->buf);
c->buf[0] = 0;

return ret;
}

void smplws_child_fini(void* vp)
{
//printf("child_fini()\n");
if (vp)free(vp);
}

//
// smplws signal
//

static int s_server_stop = 0;

void smplws_server_stop()
{
s_server_stop = 1;
}



//
// smplws_main loop
//
void smplws_server_main(){

int fd[MAX_FD];
void* vp[MAX_FD];
int tm[MAX_FD];
int ret,t;
int i,j;

s_server_stop = 0;
for (i = 0; i < MAX_FD; i++)fd[i] = -1;

fd[0] = dualsock_server_create(PORT);
if (fd[0] == -1)return;
printf("enter main loop\n");
while (1){
if (s_server_stop)break;
dualsock_select_multi(fd,MAX_FD,3);
t = smplws_gettime();
// client time out
for (i = 1; i < MAX_FD; i++){
if (fd[i] != -1 && (t - tm[i])>RECV_TIMEOUT){
closesocket(fd[i]);
smplws_child_fini(vp[i]);
fd[i] = -1;
vp[i] = NULL;
tm[i] = 0;
}
}
// polling sockets
for (i = 0; i < MAX_FD; i++){
if (fd[i] == -1)continue;
ret = dualsock_select(fd[i], 0);
if (ret == 0)continue;
if (i == 0){
int s;
void* v;

s = dualsock_accept(fd[0]);
v = smplws_child_init();
if (v == NULL){
closesocket(s);
}
for (j = 1; j < MAX_FD; j++){
if (fd[j] == -1){
// initial child
fd[j] = s;
vp[j] = v;
tm[j] = t;
break;
}
}
if (j == MAX_FD){
smplws_child_fini(v);
closesocket(s);
}
}else{
//recv child data
tm[i] = t;
ret = smplws_child_data(vp[i], fd[i]);
if (ret == -1){
closesocket(fd[i]);
smplws_child_fini(vp[i]);
fd[i] = -1;
vp[i] = NULL;
tm[i] = 0;
}
}
}
}

// abort
closesocket(fd[0]);
for (i = 1; i < MAX_FD;i++){
if (fd[i]!=-1)smplws_child_fini(vp[i]);
closesocket(fd[i]);
fd[i] = -1;
vp[i] = NULL;
}
return;
}


//
// main
//
#if 1

int main()
{
smplws_init();
smplws_server_main();
smplws_done();
return 0;
}
#endif

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

2016年3月24日木曜日

シングルトンを作る

おじさん、シングルトンについて勉強しました。
プログラムでインタフェースの勉強をすると一番最初に出てくるあれです。

C/C++でシングルトンを作るのって本当に難しいです。
これでもかというほど難しいのです。
おじさん、40歳を過ぎても、いまだにシングルトンを正しくかけず。


そこで本日はC++で何パターンかシングルトンを自分で作ってみました。

パターン3は遅いからよくないのはわかるんですが、じゃあどう書けばよいのかが全く分かりません。

ネットではパターン5でないといけないっぽいことが書かれているんですが、意味わからないし、読みづらいし、めんどくさいし・・・。

Googleの人の書いているAndroidのソースコードを見るとパターン6っぽいことをやっているので、僕的にはパターン6で行ける感じがするんですが、パターン6はそもそも本当に正しいのかなぁ?

昔は簡単でよかったんですけどね・・・・。何も悪いことしていないのになぜか僕の給料とシングルトンは勝手にどんどん状況が悪くなっています。
いまだにおじさん、シングルトンわからずです。もう引退かな。



------------------------------------
#include <stdio.h>
#include <thread>
#include <mutex>
#include <atomic>



class A{
public:
A();
virtual ~A();
static A* getInstance_1();
static A* getInstance_2();
static A* getInstance_3();
static A* getInstance_4();
static A* getInstance_5();
static A* getInstance_6();
};

A::A(){
printf("A::A() start\n");
std::this_thread::sleep_for(std::chrono::seconds(5));
printf("A::A() end\n");
}


A::~A(){
printf("A::~A()\n");
}


//
// Ptn1 OK  Static Initialization
//

A* A::getInstance_1()
{
static A s_a;
return &s_a;
}

//
// Ptn2 NG  Dynamic Initialization
//

A* A::getInstance_2()
{
static A* s_a = NULL;
if (s_a == NULL){
s_a = new A();
}
return s_a;
}


//
// Ptn3 OK  Mutex Locked Dynamic Initialization
//


A* A::getInstance_3()
{
static A* s_a = NULL;
static std::mutex mutex;

mutex.lock();
if (s_a == NULL){
s_a = new A();
}
mutex.unlock();
return s_a;
}



//
// Ptn4 NG  Double Checked Locking
//

A* A::getInstance_4()
{
static A* s_a = NULL;
static std::mutex mutex;
if (s_a == NULL){
mutex.lock();
if (s_a == NULL){
s_a = new A();
}
mutex.unlock();
}
return s_a;
}

//
// Ptn5 OK  Atomic Double Checked Locking
//

A* A::getInstance_5() {
static std::atomic<A*> s_a;
static std::mutex mutex;

A* tmp = s_a.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(mutex);
tmp = s_a.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new A();
std::atomic_thread_fence(std::memory_order_release);
s_a.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}


//
// Ptn6 OK Call Once
//


static A* s_a = NULL;
std::once_flag once_a;
void init_a(){
s_a = new A();
}

A* A::getInstance_6()
{
std::call_once(once_a, init_a);
return s_a;
}



//
// Test Code
//


void thread_main(){

std::this_thread::sleep_for(std::chrono::seconds(1));
printf("thread_main get start\n");
A* a = A::getInstance_6();
printf("thread_main get end\n");
}

int main()
{
A* a;

std::thread th(thread_main);
a = A::getInstance_6();

th.join();
delete a;

}

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





2016年2月26日金曜日

C++の共有ライブラリをdlsymで呼び出す


C++で共有ライブラリを作って、dlsym()でクラスメソッドを動的に呼び出す場合、どうすればよいのか調べてみました。

ネットでよく出回っている方法は、MicrosoftがComponentObjectModelで昔よく使っていたクラスメソッドにすべてvirtualをつけて宣言し、クラスを作ったり破棄したりするC言語形式のファクトリー関数を用意する方法です。


------------------------------------
#ifdef _WIN32
#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif
#else
#define CPPDLL_API
#endif

class Ccppdll {
public:
Ccppdll();
virtual ~Ccppdll();

virtual void test1(void);
};

#ifdef __cplusplus
extern "C"{
#endif
//factory


CPPDLL_API Ccppdll* create_cppdll();
CPPDLL_API void destroy_cppdll(Ccppdll*);

#ifdef __cplusplus
}
#endif



Ccppdll::Ccppdll()
{
printf("Ccppdll::Ccppdll()\n");
}

Ccppdll::~Ccppdll()
{
printf("Ccppdll::~Ccppdll()\n");
}

void Ccppdll::test1()
{
printf("Ccppdll::test1\n");
}

Ccppdll* create_cppdll()
{
return new Ccppdll();
}
void destroy_cppdll(Ccppdll* a)
{
delete a;
}

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

上記の例で説明すると
dlsym()でcreate_cppdll()のアドレスを取得して関数を呼び出せば、あとはCcppdllのポインタ経由でメンバー変数と仮想関数の呼び出しができます。

virtualで宣言したメンバ関数はコンストラクタで作られる仮想関数テーブル経由で関数が呼び出されるので、共有ライブラリ内でのシンボル名が解決していなくてもよいんです。

でもC++のクラスって、オペレーター演算子の関数が定義されてたり、テンプレートが使われていたりします。長年の疑問だったのですが、そういう場合、動的ロードできるのか調べてみました。


まず、オペレータ演算子のメソッド関数。
この関数そもそもvirtualにできるんですかね?
調べてみた結果、なんとできます。
すごいねぇ、オペレーターメソッド関数って仮想関数にできるのね。

次にテンプレート関数。
この関数はvirtualにできません。
そりゃーそうでしょう。だってテンプレートだから、C++のクラスの仮想関数テーブルの関数の順番が特定できないからね。
でも、テンプレート関数はstaticな無名外部関数に展開されるはずなので、テンプレート関数内部で使っている関数がすべて仮想関数ならばコンパイルが通り動的にロードできます。


実験してみました。
------------------------------------
#ifdef _WIN32
#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif
#else
#define CPPDLL_API
#endif

class Ccppdll {
public:

//ファクトリーで作る
Ccppdll();
virtual ~Ccppdll();

//呼び出せる
virtual void test1(void);
virtual int operator+(Ccppdll &a);

//呼び出せない
void test2(void);
int operator-(Ccppdll &a);

//呼び出せない
static void test3(void);

//コンパイルエラー
//template<typename t> virtual void test4(t);
//template<typename t> virtual int operator*(t);

//呼び出せる
template<typename t> void test5(t);

//呼び出せない
template<typename t> void test6(t);
};

template<typename a> void Ccppdll::test5(a){
test1();
}

template<typename a> void Ccppdll::test6(a){
test2();
}

#ifdef __cplusplus
extern "C"{
#endif

//factory
CPPDLL_API Ccppdll* create_cppdll();
CPPDLL_API void destroy_cppdll(Ccppdll*);

#ifdef __cplusplus
}
#endif


Ccppdll::Ccppdll()
{
printf("Ccppdll::Ccppdll()\n");
}

Ccppdll::~Ccppdll()
{
printf("Ccppdll::~Ccppdll()\n");
}

void Ccppdll::test1()
{
printf("Ccppdll::test1\n");
}
void Ccppdll::test2()
{
printf("Ccppdll::test2\n");
}

void Ccppdll::test3()
{
printf("Ccppdll::test2\n");
}


int Ccppdll::operator+(Ccppdll& a)
{
return 123;
}

int Ccppdll::operator-(Ccppdll& a)
{
return 456;
}

Ccppdll* create_cppdll()
{
return new Ccppdll();
}
void destroy_cppdll(Ccppdll* a)
{
delete a;
}

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




2016年2月25日木曜日

MySQLとSQLiteを切り替える

今週はデータベースの勉強期間なのでデータベースネタをもう一つ。たぶんこれでデータベース系は最後です。

僕、SQLiteって軽くて高速なので、大好きでよく使います。
だけれど、スマートフォンなどのクライアント側はSQLiteが主に使われているのに、サーバ側はMySQLがメインで、使用するデータベースが違っていてサーバとクライアントで共通化できなくて困ります。
SQLiteでさくっとプロトタイプを作って、あとからMySQLに移行してリプリケーションさせてスケールさせることもよくあります。
MySQLって商用のサーバ運用するときにはとっても便利なんですが、ローカルでWebAPIとかを開発するときは自分のPCにインストールするのがめんどくさい。

また、どの言語のどのインタフェースで書くかにもよるのですが、MySQLとSQLiteの両方で動くようにするには関数のインタフェースって微妙に違っていて困ります。JavaだとJDBCがあるのですが、JDOとかPDOとかODBCとか共通化規格がそもそも共通化されてないし。データベースってSQL文投げて複数の行が返ってくるだけなのに、なんでこう関数が違うんでしょうね。簡単なラッパーでよいんでない?

ということで、MySQLとSQLiteを直接切り替えて呼び出せるラッパー関数を作ってみました。僕は高速に動作するC/C++が大好きなので、実装はいつものようにC/C++ですが、ほかの言語でも同じようにすれば複数のデータベースを併用することができます。

前回のブログで書いたMySQLの関数の動的呼び出しを行っているので、ビルド時にMySQLのライブラリはいりません。

これで簡単にデータベースを切り替えられる!
ローカルで開発とかデバッグするときはSQLiteで行い、最終運用はMySQLで行うというようにデータベースを使い分けることや、サーバとクライアントでコードを共通化することもできます。
C言語なのでAndroid、iPhone、Linuxサーバ、Windowsサーバどれでも動くしとても便利。



mysql_sqlite.h
------------------------------------------------------------
#ifndef MY_SQL_SQLITE_H_
#define MY_SQL_SQLITE_H_


#ifdef __cplusplus
extern "C"
{
#endif

typedef void* HDBSQL;
typedef void* HDBCOL;


HDBSQL mydb_connect(const char* server,const char* user,const char* pass,const char* dbname);
void mydb_close(HDBSQL hdb);

int mydb_exec(HDBSQL hdb,const char* str);
HDBCOL mydb_prepare(HDBSQL hdb, const char* str);
int mydb_step(HDBCOL col);
void mydb_free_result(HDBCOL col);

int mydb_column_int(HDBCOL col,int n);
const char* mydb_column_char(HDBCOL col,int n);


#ifdef __cplusplus
}
#endif

#endif /* MY_SQL_SQLITE_H_ */
------------------------------------------------------------


mysql_sqlite.c
------------------------------------------------------------
//#define I_USE_MYSQL
#define I_USE_MYSQL_WRAPPER
#define I_USE_SQLITE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef I_USE_MYSQL
#ifdef I_USE_MYSQL_WRAPPER
#include "mysql_wrapper.h"
#else
#include <mysql/mysql.h>
#endif
#endif

#ifdef I_USE_SQLITE
#include "sqlite3.h"
#endif

#include "mysql_sqlite.h"

HDBSQL mydb_connect(const char* server, const char* user, const char* pass, const char* dbname)
{
HDBSQL ret = NULL;
if (server == NULL || user == NULL || pass == NULL || dbname == NULL)goto end;
#ifdef I_USE_MYSQL
MYSQL *conn = NULL;
conn = mysql_init(NULL);
if (!mysql_real_connect(conn, server, user, pass, dbname, 0, NULL, 0)) {
goto end;
}
ret = (HDBSQL)conn;
#endif
#ifdef I_USE_SQLITE
sqlite3 *conn = NULL;
if(SQLITE_OK != sqlite3_open("db_test", &conn)){
goto end;
}
ret = (HDBSQL)conn;
#endif
end:
return ret;
}
void mydb_close(HDBSQL hdb)
{
if (hdb == NULL)return;
#ifdef I_USE_MYSQL
mysql_close((MYSQL*)hdb);
#endif
#ifdef I_USE_SQLITE
sqlite3_close((sqlite3*)hdb);
#endif
}

int mydb_exec(HDBSQL hdb, const char* str)
{
int ret = -1;
if (hdb == NULL || str==NULL)return ret;
#ifdef I_USE_MYSQL
if (mysql_query((MYSQL*)hdb, str)){
//error
ret = -1;
}else {
//ok
ret = 0;
}
#endif
#ifdef I_USE_SQLITE
if (SQLITE_OK != sqlite3_exec((sqlite3*)hdb, str, NULL, NULL, NULL))
{
ret = -1;
}else {
ret = 0;
}

#endif
return ret;
}


struct hdb {
void* resp;
void* row;
};

HDBCOL mydb_prepare(HDBSQL hdb, const char* str)
{
HDBCOL ret = NULL;
if (hdb == NULL || str == NULL)goto end;

#ifdef I_USE_MYSQL
ret = (HDBCOL)malloc(sizeof(struct hdb));
if (ret == NULL)goto end;
memset(ret, 0, sizeof(struct hdb));

if (mysql_query((MYSQL*)hdb, str)) {
//error
free(ret);
goto end;
}
((struct hdb*)ret)->resp=(HDBCOL)mysql_use_result((MYSQL*)hdb);
#endif
#ifdef I_USE_SQLITE
if (SQLITE_OK != sqlite3_prepare((sqlite3*)hdb, str, strlen(str), (sqlite3_stmt**)(&ret), NULL))
{
ret = NULL;
}
#endif
end:
return ret;
}

void mydb_free_result(HDBCOL col)
{
if (col == NULL)return;
#ifdef I_USE_MYSQL
mysql_free_result((MYSQL_RES*)(((struct hdb*)col)->resp));
free(col);
#endif
#ifdef I_USE_SQLITE
sqlite3_finalize((sqlite3_stmt*)col);
#endif
}

int mydb_step(HDBCOL col)
{
int ret = 0;
if (col == NULL)return ret;
#ifdef I_USE_MYSQL
((struct hdb*)col)->row= mysql_fetch_row((MYSQL_RES*)(((struct hdb*)col)->resp));
if (((struct hdb*)col)->row == NULL)return ret;
ret = 1;
#endif
#ifdef I_USE_SQLITE
if (SQLITE_ROW == sqlite3_step((sqlite3_stmt*)col))ret = 1;
#endif
return ret;
}

int mydb_column_int(HDBCOL col,int n)
{
int ret = 0;
const char* p = NULL;
if (col == NULL)return ret;
#ifdef I_USE_MYSQL
if (((struct hdb*)col)->row == NULL)return ret;
p=((MYSQL_ROW)(((struct hdb*)col)->row))[n];
if (p == NULL)p = "0";
ret = atoi(p);
#endif
#ifdef I_USE_SQLITE
ret= sqlite3_column_int((sqlite3_stmt*)col, n);
#endif
return ret;

}
const char* mydb_column_char(HDBCOL col,int n)
{
const char* ret = NULL;
#ifdef I_USE_MYSQL
if (((struct hdb*)col)->row == NULL)return ret;
if (((struct hdb*)col)->row == NULL)return ret;
ret = ((MYSQL_ROW)(((struct hdb*)col)->row))[n];
#endif
#ifdef I_USE_SQLITE
ret = sqlite3_column_text((sqlite3_stmt*)col, n);
#endif
return ret;
}

#if 0

int main(void) {
HDBSQL conn = NULL;
HDBCOL resp = NULL;

char sql_str[255];
char *sql_serv = "localhost";
char *user = "root";
char *passwd = "wanted";
char *db_name = "db_test";


conn = mydb_connect(sql_serv,user,passwd,db_name);
if (conn == NULL) {
printf("mydb_connect error\n");
exit(-1);
}

memset(&sql_str[0], 0x00, sizeof(sql_str));
snprintf(&sql_str[0], sizeof(sql_str) - 1, "select * from tb_test");
resp = mydb_prepare(conn, sql_str);
if (resp==NULL) {
mydb_close(conn);
exit(-1);
}

while (mydb_step(resp)){
printf("%d : %s\n", mydb_column_int(resp,0),mydb_column_char(resp,1));
}
mydb_free_result(resp);
mydb_close(conn);

return 0;
}

#endif
------------------------------------------------------------





2016年2月22日月曜日

MySQLを動的にリンクする

久しぶりにデータベースの勉強をしようと思い僕のWindowsマシンにMySQLを入れてみました。
ところがMySQLをC言語から使おうとするとVisualStudio2013までしかサポートしていません。
せっかく最新のVisualStudio2015をいれたのに。これでは最新の開発でMySQL使えないじゃん。

このようなVisualStudioやGCCのバージョン依存問題って本当によく起こるのでこれらを解決する方法を考えてみました。

考えること一時間、MySQLの関数を動的にリンクして呼び出せばよいのではないかと思い、MySQLの動的呼び出しラッパーをつくってみました。

余談ですが、MySQLのコード書いている人、とても正しくきれいにコーディングできていてすごいですね。びっくりです。OpenSSLなんて汚くて汚くて。


mysql_wrapper.h
----------------------------------------
#ifndef MY_SQL_WRAPPER_H_
#define MY_SQL_WRAPPER_H_

#include <mysql/mysql.h>

#ifdef __cplusplus
extern "C"
{
#endif

extern MYSQL * (STDCALL *pf_mysql_init)(MYSQL *mysql);
extern void (STDCALL *pf_mysql_close)(MYSQL *sock);
extern MYSQL * (STDCALL *pf_mysql_real_connect)(MYSQL *mysql, const char *host,
const char *user,const char *passwd,const char *db,
unsigned int port,const char *unix_socket,unsigned long clientflag);
extern int (STDCALL *pf_mysql_query)(MYSQL *mysql, const char *q);
extern MYSQL_RES * (STDCALL *pf_mysql_use_result)(MYSQL *mysql);
extern void (STDCALL *pf_mysql_free_result)(MYSQL_RES *result);
extern MYSQL_ROW(STDCALL *pf_mysql_fetch_row)(MYSQL_RES *result);


#ifdef __cplusplus
}
#endif

#ifndef MY_SQL_WRAOPPER_C

#define mysql_init pf_mysql_init
#define mysql_close pf_mysql_close
#define mysql_real_connect pf_mysql_real_connect
#define mysql_query pf_mysql_query
#define mysql_use_result pf_mysql_use_result
#define mysql_free_result pf_mysql_free_result
#define mysql_fetch_row pf_mysql_fetch_row

#endif /* MY_SQL_WRAOPPER_C */

#endif /* MY_SQL_WRAPPER_H_ */
----------------------------------------


mysql_wrapper.cpp
----------------------------------------
#define MY_SQL_WRAOPPER_C
#include "mysql_wrapper.h"
#include "dll_client.h"

MYSQL * (STDCALL *pf_mysql_init)(MYSQL *mysql)=NULL;
void (STDCALL *pf_mysql_close)(MYSQL *sock)=NULL;
MYSQL * (STDCALL *pf_mysql_real_connect)(MYSQL *mysql, const char *host,
const char *user,const char *passwd,const char *db,unsigned int port,
const char *unix_socket,unsigned long clientflag) = NULL;
int (STDCALL *pf_mysql_query)(MYSQL *mysql, const char *q)=NULL;
MYSQL_RES * (STDCALL *pf_mysql_use_result)(MYSQL *mysql)=NULL;
void (STDCALL *pf_mysql_free_result)(MYSQL_RES *result)=NULL;
MYSQL_ROW(STDCALL *pf_mysql_fetch_row)(MYSQL_RES *result) = NULL;


static void *h_mysql = NULL;

class mysql_wrapper {
public :
mysql_wrapper();
virtual ~mysql_wrapper();
};

mysql_wrapper::mysql_wrapper()
{
h_mysql = dll_load(DLL_NAME("libmysql"));
if (h_mysql == NULL)return;
pf_mysql_init = (MYSQL * (STDCALL *)(MYSQL *))dll_access(h_mysql, "mysql_init");
pf_mysql_close = (void (STDCALL *)(MYSQL *))dll_access(h_mysql, "mysql_close");
pf_mysql_real_connect = (MYSQL * (STDCALL *)(MYSQL *, const char *,
const char *, const char *, const char *, unsigned int ,
const char *, unsigned long))dll_access(h_mysql, "mysql_real_connect");
pf_mysql_query = (int (STDCALL *)(MYSQL *mysql, const char *))dll_access(h_mysql, "mysql_query");
pf_mysql_use_result = (MYSQL_RES * (STDCALL *)(MYSQL *))dll_access(h_mysql, "mysql_use_result");
pf_mysql_free_result = (void (STDCALL *)(MYSQL_RES *))dll_access(h_mysql, "mysql_free_result");
pf_mysql_fetch_row = (MYSQL_ROW(STDCALL*)(MYSQL_RES *))dll_access(h_mysql, "mysql_fetch_row");
}

mysql_wrapper::~mysql_wrapper()
{
if (h_mysql)dll_close(h_mysql);
h_mysql = NULL;
}


static mysql_wrapper s_mysql_wrapper;




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

test.c
----------------------------------------
#define I_USE_MYSQL_WRAPPER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef I_USE_MYSQL_WRAPPER
#include "mysql_wrapper.h"
#else
#include <mysql/mysql.h>
#endif


int main(void) {
MYSQL *conn = NULL;
MYSQL_RES *resp = NULL;
MYSQL_ROW row;
char sql_str[255];
char *sql_serv = "localhost";
char *user = "root";
char *passwd = "hoge";
char *db_name = "db_test";

memset(&sql_str[0], 0x00, sizeof(sql_str));

// mysql接続
conn = mysql_init(NULL);

if (!mysql_real_connect(conn, sql_serv, user, passwd, db_name, 0, NULL, 0)) {
// error
exit(-1);
}

// クエリ実行
snprintf(&sql_str[0], sizeof(sql_str) - 1, "select * from tb_test");
if (mysql_query(conn, &sql_str[0])) {
// error
mysql_close(conn);
exit(-1);
}

// レスポンス
resp = mysql_use_result(conn);

while ((row = mysql_fetch_row(resp)) != NULL) {
printf("%d : %s\n", atoi(row[0]), row[1]);
}

// 後片づけ
mysql_free_result(resp);
mysql_close(conn);
return 0;
}

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

やったー、動いた。
テストプログラムもちゃんと動きました。

この方法、OpenCVなどC++で書かれているライブラリだと使えませんが、
OpenSSLなどC言語で書かれているほかのライブラリでも使えます。

2016年2月16日火曜日

セキュアでないセキュアプログラミング

お仕事でプログラムをするときは、たとえば長い文字列を渡されてプログラムが攻撃されても大丈夫なようにセキュアプログラミングをしないといけません。
言語ごとにセキュアプログラミングのポイントが異なるのですが、とくにC言語はセキュアにプログラムをするのが本当に難しいです。

例えばアップルとかは以下のガイドラインを出してます。
セキュアコーディングガイド - Apple Developer

これにもC言語でセキュアプログラミングをする場合、「sprintf()、vsprint()でなくてsnprintf()、vsnprint()を使いましょう」って書いてあります。

でもこれ嘘です。

たとえば、Visual Studio 2013のC++のsnprintf()、vsnprint()は文字列が指定したサイズをオーバーしそうになった場合、文字列をnullで終端しません。

なのでsnprintf()を使って文字列を切り捨てているLinuxアプリをWindowsに移植するとへんな文字列が表示されたりプログラムが落ちます。

ANSI規格でnull終端する決まりになっていますが、決まりができるより前からあるVisualStudioではそうなっていないのです。

MSDNにも以下のように書かれています。
----------------------
Visual Studio 2015 その他のバージョン
解説
これらの関数は、引数リストへのポインターを使用し、データを書式指定して count 文字数までの文字を buffer が指すメモリに書き込みます。終端に空きがある場合 (つまり、書き込む文字数が count 文字数未満の場合)、バッファーは null で終わります。
----------------------

なんでしょうね。このへたくそな解説と翻訳。
一見nullで終わりますって書いてあるけど、よく読むと条件付きでnullで終わります。
つまり、nullでおわるとは限らないのです。
英語文化圏の人にはわかりやすいんでしょうがこれが日本語の壁というかなんというか。

さらにひどい会社とかのルールになるとstrcpy()、strcat()の代わりにstrncpy()、strncat()を使いましょうっていうのもあります。

もうこのルール作ったの誰だよ!これもそうとう怪しいです。
だってstrncpy()もVisualStudioのsnprintf()と同様にnullで終端しない可能性があるし、strncat()にいたっては連結する最大文字列であってバッファの最大サイズではないんです。
strcpy()、strcat()を使ってはいけないまではあっているのに・・・・。
もう、何が安全なのかさっぱりわかりません。


C言語のセキュアプログラミングについてどれも信用できないし、strcpy()、strcat()、sprintf()、snprintf()の関数って結構便利で使いたいですよね。
C言語の場合どの解説もよい代替案がないので、それが問題なんですよね。

解決策として、マイクロソフトはマイクロソフトがセキュリティー開発ライフサイクル(SDL)プログラミングっていうのを推奨しています。
これによると、マイクロソフトはstrcpy_s()、strcat_s()、sprintf_s()、snprintf_s()という関数を使用することを推奨していますが、これってLinuxにないので困ります。

また、アップルは上記にあげた先ほどのガイドラインで、strlcpy()とstrlcat()を使うことを推奨しています。これもMacOSXとかiPhoneでしか動かない。

なので、「僕だったらこう作ります。」というstrcpy_y()、strcat_y()、sprintf_y()を作ってみました。
基本的にはアップルと同じものをアップル以外にも動くようにした感じで、マイクロソフトの良いところも取り入れてみました。

こう書けばどのOSでも動くし、#include"safestring.h"すれば出力バッファが関数内で定義されているという条件付きですがstrcat()とかsprintf()とかそのままでも結構安全に動きます。

一流企業でもセキュアプログラミングの方針が全く違うので、C言語での標準化なんて出来そうにないですね。やはりC言語でのセキュアプログラミングのやり方って難しいんですね。

safestring.h
-------------------------------
#ifndef df_SAFESTRING_H_
#define df_SAFESTRING_H_


#define strcpy(a,b) strcpy_y(a,sizeof(a),b)
#define strcat(a,b) strcat_y(a,sizeof(a),b)
#define sprintf(a,b,...) sprintf_y(a,sizeof(a),b,__VA_ARGS__)
#define snprintf(a,b,fmt,...) sprintf_y(a,b,fmt,__VA_ARGS__)

#ifdef __cplusplus
extern "C"
{
#endif


char* strcpy_y(char* a,int sz,const char* b);
char* strcat_y(char* a, int sz, const char* b);
int sprintf_y(char* a,int sz,const char* fmt,...);

#ifdef __cplusplus
}
#endif


#endif /* df_SAFESTRING_H_ */
-------------------------------




safestring.c
-------------------------------
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "safestring.h"

#ifdef _MSC_VER
#if _MSC_VER >= 1400
#pragma warning( disable : 4996 )
#pragma warning( disable : 4819 )
#endif
#endif


char* strcpy_y(char* a, int sz, const char* b)
{
if (a == NULL || b == NULL || sz<1)return a;

*a = 0;
strncpy(a, b, sz);
a[sz - 1] = 0;
return a;
}


char* strcat_y(char* a, int sz, const char* b)
{
int l;
if (a == NULL || b == NULL || sz<1)return a;

l=sz-1-strlen(a);
if (l< 1)return a;

strncat(a, b, l);
a[sz - 1] = 0;
return a;
}

int sprintf_y(char* a, int sz, const char* fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret=vsnprintf(a, sz, fmt, ap);
a[sz - 1] = 0;
//va_end(ap);
return ret;
}

static int snprintf_bug(char* a, int sz, const char* fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = vsnprintf(a, sz, fmt, ap);
//a[sz - 1] = 0;
//va_end(ap);
return ret;
}

#if 1

int main(int argc, char* argv[])
{
char a[8];
char* p;
p = a;
char b[16];

strcpy(a, "1234567890");
printf("a=>>%s<<\n",a);

strcpy(a, "a");
strcat(a, "bcdefgihj");
printf("a=>>%s<<\n", a);

strcpy(p, "a");
strcat(p, "bcdefgihj");
printf("a=>>%s<<\n", a);

sprintf(a, "--%d--", 123456);
printf("a=>>%s<<\n", a);

snprintf(a, sizeof(a), "++%d++", 123456);
printf("a=>>%s<<\n", a);

strcpy(b, "###############");
snprintf_bug(b, 8, "--%d--", 123456);
printf("b=>>%s<<\n", b);

return 0;
}
#endif

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