node-gyp

Node.jsでは、C++で拡張モジュールを作ることができる。
この拡張モジュールを経由して、LinuxならシステムコールWindowsならWin32APIなどを呼び出すことができる。

GitHub - nodejs/node-gyp: Node.js native addon build tool
Node.jsのネイティブ拡張を作ってみよう 〜NAN, 非同期処理, npm公開まで〜 - Qiita

今回は、node-gypを用いて、簡単な拡張モジュールを作る。

まずは、npm init。

$ pwd
/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext
$ ls
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (native_ext)
version: (1.0.0)
description: node-gyp sample
entry point: (index.js) main.js
test command: start
git repository: -
keywords: -
author: B.G
license: (ISC) MIT
About to write to
/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/package.json:

{
  "name": "native_ext",
  "version": "1.0.0",
  "description": "node-gyp sample",
  "main": "main.js",
  "scripts": {
    "test": "start"
  },
  "repository": {
    "type": "git",
    "url": "-"
  },
  "keywords": [
    "-"
  ],
  "author": "B.G",
  "license": "MIT"
}


Is this OK? (yes)
$

続いて、node-gypをインストール。

$ npm install node-gyp
npm notice created a lockfile as package-lock.json. You should commit this file.
+ node-gyp@3.8.0
added 97 packages from 67 contributors and audited 183 packages in 6.812s
found 0 vulnerabilities



   ╭───────────────────────────────────────────────────────────────╮
   │                                                               │
   │       New minor version of npm available! 6.4.1 → 6.9.0       │
   │   Changelog: https://github.com/npm/cli/releases/tag/v6.9.0   │
   │               Run npm install -g npm to update!               │
   │                                                               │
   ╰───────────────────────────────────────────────────────────────╯

$

Python2.7(3系は非対応)が必要なのだが、入ってないので入れる。

$ which python
/usr/bin/which: no python in
(/home/bg1/.local/bin:/home/bg1/bin:/home/bg1/.local/bin:/home/bg1/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin)
$ sudo yum install python
メタデータの期限切れの最終確認: xx:xx:xx 時間前の 2019年xx月xx日 xx時xx分xx秒 に実施しました。
依存関係が解決しました。
=======================================================================================================
 パッケージ                          アーキテクチャー
                                                     バージョン
    リポジトリ       サイズ
=======================================================================================================
インストール:
 python-unversioned-command          noarch          2.7.15-11.fc29
         updates           13 k
依存関係をインストール中:
 python2                             x86_64          2.7.15-11.fc29
         updates           46 k
 python2-libs                        x86_64          2.7.15-11.fc29
         updates          6.1 M
 compat-openssl10                    x86_64          1:1.0.2o-3.fc29
         fedora           1.1 M
 gdbm                                x86_64          1:1.18-1.fc29
         fedora           116 k
弱い依存関係をインストール中:
 python2-pip                         noarch          18.1-1.fc29
         updates          1.9 M
 python2-setuptools                  noarch          40.8.0-1.fc29
         updates          644 k

トランザクションの概要
=======================================================================================================
インストール  7 パッケージ

ダウンロードサイズの合計: 10 M
インストール済みのサイズ: 41 M
これでよろしいですか? [y/N]: y
パッケージのダウンロード中です:
(1/7): python2-2.7.15-11.fc29.x86_64.rpm
 24 kB/s |  46 kB     00:01
(2/7): python-unversioned-command-2.7.15-11.fc29.noarch.rpm
6.9 kB/s |  13 kB     00:01
(3/7): python2-setuptools-40.8.0-1.fc29.noarch.rpm
156 kB/s | 644 kB     00:04
(4/7): compat-openssl10-1.0.2o-3.fc29.x86_64.rpm
728 kB/s | 1.1 MB     00:01
(5/7): python2-libs-2.7.15-11.fc29.x86_64.rpm
694 kB/s | 6.1 MB     00:09
(6/7): gdbm-1.18-1.fc29.x86_64.rpm
 58 kB/s | 116 kB     00:01
(7/7): python2-pip-18.1-1.fc29.noarch.rpm
228 kB/s | 1.9 MB     00:08
-------------------------------------------------------------------------------------------------------
合計
732 kB/s |  10 MB     00:13
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
  準備             :
                          1/1
Installed: gdbm-1:1.18-1.fc29.x86_64
  インストール中   : gdbm-1:1.18-1.fc29.x86_64
                     1/7
Installed: gdbm-1:1.18-1.fc29.x86_64
Installed: compat-openssl10-1:1.0.2o-3.fc29.x86_64
  インストール中   : compat-openssl10-1:1.0.2o-3.fc29.x86_64
                     2/7
  scriptletの実行中: compat-openssl10-1:1.0.2o-3.fc29.x86_64
                        2/7
Installed: compat-openssl10-1:1.0.2o-3.fc29.x86_64
Installed: python2-libs-2.7.15-11.fc29.x86_64
  インストール中   : python2-libs-2.7.15-11.fc29.x86_64
                     3/7
Installed: python2-libs-2.7.15-11.fc29.x86_64
Installed: python2-pip-18.1-1.fc29.noarch
  インストール中   : python2-pip-18.1-1.fc29.noarch
                     4/7
Installed: python2-pip-18.1-1.fc29.noarch
Installed: python2-setuptools-40.8.0-1.fc29.noarch
  インストール中   : python2-setuptools-40.8.0-1.fc29.noarch
                     5/7
Installed: python2-setuptools-40.8.0-1.fc29.noarch
Installed: python2-2.7.15-11.fc29.x86_64
  インストール中   : python2-2.7.15-11.fc29.x86_64
                     6/7
Installed: python2-2.7.15-11.fc29.x86_64
Installed: python-unversioned-command-2.7.15-11.fc29.noarch
  インストール中   : python-unversioned-command-2.7.15-11.fc29.noarch
                     7/7
Installed: python-unversioned-command-2.7.15-11.fc29.noarch
  scriptletの実行中: python-unversioned-command-2.7.15-11.fc29.noarch
                        7/7
  検証             : python-unversioned-command-2.7.15-11.fc29.noarch
                          1/7
  検証             : python2-2.7.15-11.fc29.x86_64
                          2/7
  検証             : python2-libs-2.7.15-11.fc29.x86_64
                          3/7
  検証             : python2-pip-18.1-1.fc29.noarch
                          4/7
  検証             : python2-setuptools-40.8.0-1.fc29.noarch
                          5/7
  検証             : compat-openssl10-1:1.0.2o-3.fc29.x86_64
                          6/7
  検証             : gdbm-1:1.18-1.fc29.x86_64
                          7/7

インストール済み:
  python-unversioned-command-2.7.15-11.fc29.noarch
python2-pip-18.1-1.fc29.noarch
  python2-setuptools-40.8.0-1.fc29.noarch
python2-2.7.15-11.fc29.x86_64
  python2-libs-2.7.15-11.fc29.x86_64
compat-openssl10-1:1.0.2o-3.fc29.x86_64
  gdbm-1:1.18-1.fc29.x86_64

完了しました!
$

binding.gypというファイルに、

と書く。
native_ext.ccというのがC++ソースファイル。
native_ext.ccには、

get_abcという自作関数を用意し、args.GetReturnValue().Setで"ABC"をセットすることで、戻り値"ABC"を返してる。
(おまじないということで詳細は後で・・・。)

node-gypコマンドを使って、configureとbuildをする。

$ vi binding.gyp
$ vi native_ext.cc
$ ls
binding.gyp  build  native_ext.cc  node_modules  package-lock.json  package.json
$ ./node_modules/.bin/node-gyp configure
gyp info it worked if it ends with ok
gyp info using node-gyp@3.8.0
gyp info using node@10.15.0 | linux | x64
gyp info spawn /usr/bin/python2
gyp info spawn args [
'/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/node_modules/node-gyp/gyp/gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'make',
gyp info spawn args   '-I',
gyp info spawn args
'/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build/config.gypi',
gyp info spawn args   '-I',
gyp info spawn args
'/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/node_modules/node-gyp/addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/home/bg1/.node-gyp/10.15.0/include/node/common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=/home/bg1/.node-gyp/10.15.0',
gyp info spawn args
'-Dnode_gyp_dir=/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/node_modules/node-gyp',
gyp info spawn args
'-Dnode_lib_file=/home/bg1/.node-gyp/10.15.0/<(target_arch)/node.lib',
gyp info spawn args
'-Dmodule_root_dir=/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext',
gyp info spawn args   '-Dnode_engine=v8',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'build',
gyp info spawn args   '-Goutput_dir=.' ]
gyp info ok
$ ./node_modules/.bin/node-gyp build
gyp info it worked if it ends with ok
gyp info using node-gyp@3.8.0
gyp info using node@10.15.0 | linux | x64
gyp info spawn make
gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ]
make: ディレクトリ '/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build'
に入ります
  CXX(target) Release/obj.target/native_ext/native_ext.o
In file included from ../native_ext.cc:2:
/home/bg1/.node-gyp/10.15.0/include/node/node.h:570:43: 警告: cast
between incompatible function types from ‘void
(*)(v8::Local<v8::Object>)’ to ‘node::addon_register_func’ {aka ‘void
(*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)’}
[-Wcast-function-type]
       (node::addon_register_func) (regfunc),                          \
                                           ^
/home/bg1/.node-gyp/10.15.0/include/node/node.h:604:3: 備考: in
expansion of macro ‘NODE_MODULE_X’
   NODE_MODULE_X(modname, regfunc, NULL, 0)  // NOLINT (readability/null_usage)
   ^~~~~~~~~~~~~
../native_ext.cc:24:1: 備考: in expansion of macro ‘NODE_MODULE’
 NODE_MODULE(native_ext, init);
 ^~~~~~~~~~~
  SOLINK_MODULE(target) Release/obj.target/native_ext.node
  COPY Release/native_ext.node
make: ディレクトリ '/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build'
から出ます
gyp info ok
$

グローバルインストール( -g)じゃないので、"./node_modules/.bin/node-gyp"の"conigure"と"build"を使って、ビルドまで行う。

$ ls
binding.gyp  build  native_ext.cc  node_modules  package-lock.json  package.json
$ cd build/
$ ls
Makefile  Release  binding.Makefile  config.gypi  native_ext.target.mk
$ cd Release/
$ ls
native_ext.node  obj.target
$ pwd
/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build/Release
$

build/Releaseの下の、"native_ext.node"がビルドで生成されたモジュール。
これを、main.jsから参照させる。

このようにnativeExt.get_abc()を呼び出せるので、戻り値の"ABC"をconsole.logで出力。
これでnodeコマンドで呼び出せるが、せっかくなので、package.jsonで、


"scripts"で"start"なら"node ."を実行するようにする。

$ vi package.json
$ npm start

> native_ext@1.0.0 start /home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext
> node .

ABC
$

npm startで起動して、取得した"ABC"を出力するようにできた。

Sample/node-gyp/node-gyp/node-gyp/src/native_ext at master · bg1bgst333/Sample · GitHub

String

Stringは文字列オブジェクト。

文字列(String)
String - JavaScript | MDN

コンストラクタに文字列を渡すと、その文字列を持つ文字列オブジェクトが生成される。

"ABC"を渡して生成されたStringオブジェクトstrをalertに渡す。

&quot;ABC&quot;が表示
"ABC"が表示

"ABC"が表示される。

Sample/js/String/String/src/String at master · bg1bgst333/Sample · GitHub

わんくま同盟 東京勉強会 #116 にて参加&LTしてきました。

わんくま同盟 東京勉強会 #116」に参加してきました。

わんくま同盟

LT資料作成のため、中さんのVisual Studio 2019の話は聞けなかったわけですが・・・。

赤坂さんの.NET Core 3.0 非同期ストリームの話は、簡単に書けるなあと思いました。

ジニアス平井さんのCognitive Serviceの話は、凄かったので使ってみたいなあと思いました・・・。

C#の歴史は長かったです・・・。えムナウさん還暦おめでとうございます。

LTは未定だったのですが、こういう機会なのでなんとかねじ込ませていただきました・・・。

www.slideshare.net

またしても、直前の資料作成突然の割り込みLTになってしまいまして、大変に申し訳ございませんでした・・・。

懇親会は話しててやはりチャレンジ精神が大事だなあと感じましたね・・・。

以上です。

PolylineTo

PolylineToは、多角形を描画する。

PolylineTo function | Microsoft Docs

直角三角形を描画する。
PolylineTo.cppのWM_CREATEで、

始点を(50, 50)として、直角三角形を描画するが、始点はMoveToExでセットするため、次の点、そのまた次の点、最後の点(これが始点と同じ)となるようにPOINT配列ptにセットしておく。

WM_PAINTで、

MoveToExで始点(50, 50)をセットしてから、PolylineToにptと直線の数である3を渡す。

直角三角形
直角三角形

直角三角形が描画されている。

Sample/winapi/PolylineTo/PolylineTo/src/PolylineTo at master · bg1bgst333/Sample · GitHub

RestoreDC

RestoreDCは、デバイスコンテキストの状態を復元する。

RestoreDC function | Microsoft Docs

SaveDCで取得した正の整数であるIDを指定すると、そのIDを取得した時の状態に戻せるが、負の整数-nを指定すると、現在からn回前の状態に復元することもできる。

RestoreDC.cppのWM_PAINTで、

-1は直前の状態なので青、 -2は2回前なので緑、 -3は3回前なので赤。

最初は、

最初
最初

黒だが、

3回前の赤
3回前の赤

3回前の赤。

2回前の緑
2回前の緑

2回前の緑。

直前の青
直前の青

直前の青。

と、このように負の数を指定しても復元できている。

Sample/winapi/RestoreDC/RestoreDC/src/RestoreDC at master · bg1bgst333/Sample · GitHub

OneLang32/OneLang32Console(0, 0, 0, 6/Nishi-Funabashi #6) -デバッグ時のコマンドライン引数対応. etc...

デバッグ時のコマンドライン引数対応するのと、ワイド文字への対応を実装した。

プロパティでコマンドライン引数を指定
プロパティでコマンドライン引数を指定

プロパティでコマンドライン引数を指定して、作業ディレクトリも暫定でセットして、これでデバッガからもtest.1が読めるようにした。

リリースビルド
リリースビルド

リリースビルドも同様に。

これはいいとして、メインはワイド文字対応。

LexicalAnalyzer.cppで、

コマンドプロンプトはワイド文字が苦手なので、ソースのコメントなどにワイド文字があっても大丈夫なように、そもそもソースそのものは出力しない。

読み込まれるテキストはCTextFileクラスによって、UTF16のワイド文字に変換されている。
その上で、0xffを超えれば、その文字はワイド文字。
トークンが空なら、ワイド文字から始まるトークンなので、先頭に"<WCS>"と付ける。
今回はそうでない場合でも、例えば、"aあ”のようにアルファベットから始まって、ワイド文字に来た場合は、先頭を見て、そうであれば、先頭に"<WCS>"を付ける。
そして、この2つのパターンの場合、トークンを連結する。
"aあaあ"の場合、2つ目の"あ"の時、トークンは空ではないし、先頭は"<WCS>"の"<"になっているはずなので、二重に"<WCS>"を付けてしまうことはない。

m_vectstrTokenListの中からfindで、"<WCS>"があるかを探し、あった場合は、"<WCS>"とだけ出力して、内容は出力しない。
それ以外は出力する。
これでコンソール出力が途中で切れたりしない。

今回から、しばらくは、C言語の"Hello, world!"をいかにOneLang風に削っていくかを試していくので、test.1は、

C言語の"Hello, world!"プログラムにした。
("/* aあaあ */"という一応先程のケースのテストコメントも入れている。)

実行すると、

argv[1] = test.1
CLexicalAnalyzer::Analyze!
----- start -----
----- end -----
/
*
<Space>
<WCS>
<Space>
*
/
<CR>
<LF>
#
include
<Space>
<
stdio
.
h
>
<Space>
/
*
<Space>
<WCS>
<Space>
*
/
<CR>
<LF>
/
*
<Space>
<WCS>
<Space>
*
/
<CR>
<LF>
/
*
<Space>
<WCS>
<Space>
*
/
<CR>
<LF>
int
<Space>
main
(
void
)
{
<Space>
/
*
<Space>
<WCS>
.
<Space>
*
/
<CR>
<LF>
<CR>
<LF>
<Space>
<Space>
/
*
<Space>
<WCS>
<Space>
*
/
<CR>
<LF>
<Space>
<Space>
printf
(
"
Hello
,
<Space>
world
!
\
n
"
)
;
<Space>
/
*
<Space>
<WCS>
"
Hello
,
<Space>
world
!
"
<WCS>
.
<Space>
*
/
<CR>
<LF>
<CR>
<LF>
<Space>
<Space>
/
*
<Space>
<WCS>
<Space>
*
/
<CR>
<LF>
<Space>
<Space>
return
<Space>
0
;
<Space>
/
*
<Space>
<WCS>
0
<WCS>
.
<Space>
*
/
<CR>
<LF>
<CR>
<LF>
}
続行するには何かキーを押してください . . .

このように、本来、ワイド文字が出力される部分には、"<WCS>"が出力されている。

-デバッグ時のコマンドライン引数対応. · bg1bgst333/OneLang32@e6f7aa5 · GitHub

SaveDC

SaveDCは、デバイスコンテキストの状態を保存する。

SaveDC function | Microsoft Docs

SetTextColorでテキストの色、SelectObjectでペンやブラシなどのGDIオブジェクトとの紐づけなど、デバイスコンテキストの状態は頻繫に変わる。
状態を保存しておけば、切り替えに手間がかからない。

SaveDC.cppのWM_COMMANDで、

メニュー項目、Red、Green、Blue、が選択されるごとに、スタティック変数whichの値を変えて画面更新。
あとはWM_PAINTに任せる。

BeginPaint直後に、SetTextColorで赤、緑、青と変えつつ、そのたびにSaveDCで保存している。
SaveDCの戻り値にIDが返り、それをRestoreDCに渡すと復元できる。
なので、最終的にwhichの値で復元する色を決めている。

最初は黒
最初は黒

最初は黒。

Red
Red

Redを選択すると、

赤になる
赤になる

赤になる。

Green
Green

Greenは、

緑になる
緑になる

緑になる。

Blue
Blue

Blueは、

青になる
青になる

青になる。

本当は、あらかじめWM_CREATEとかでSaveDCしてWM_PAINTはRestoreDCだけだったり、1回目の選択でSetTextColorしてSaveDCし、2回目以降はRestoreDCだけするようにしてみたが、デバイスコンテキストハンドルはWM_PAINTするたびに変わってしまい、SaveDCしてもデバイスコンテキストが違うので、状態がスタックしない。
なので、今回はBeginPaintからEndPaintまでの間でSaveDCを繰り返した。
やってみて、これだとSelectObjectやSetTextColorを繰り返すより手間が減るかというと微妙であると感じた・・・。
まあ、量が多ければ減るとはおもうが・・・。

Sample/winapi/SaveDC/SaveDC/src/SaveDC at master · bg1bgst333/Sample · GitHub