江添 C++ 本と本家ストラウストラップ本

プロになる Java』で Java の教科書的な書籍を取り上げたので、今度は C++ で。

当初、江添 C++ 本のみ取り上げようと思ったんだが、やっぱり(世にいう)ストラウストラップ本も必要かな?と思い、最初の版よりかなり内容変わってます。

ある程度実用的に C や C++ 使うんだったら、両方必要でしょう。

ワイの場合、C はまあまあ使えていたから、ここら辺から入ったが、C 未習の人は、この前に C の基礎をやっておいた方がいいでしょう。

江添 C++ 本

しっかり書かれた本ならなんでもいいと思うのだが、取り組みやすさを考えるならば『江添亮の C++ 入門』あたりがオススメ。

取り組みやすさ、というのは、この本のイントロが GitHub で公開されている、というのもその理由の一つ。

標準ライブララリを一つのヘッダファイルでまとめる話が第 2 章で出てくるが、こんなのいちいち打ち込むわけにもいかない。ここが該当箇所。

まあ、ならサンプルコード一覧つけてくれよ、と思わないでもないが。

これはケチをつけているわけではなく、Mac でこのコードを動かそうとしても動かない場合があるから。

‘cstdalign’ file not found エラーの回避方法

ワイの環境の場合、

‘cstdalign’ file not found

というエラーが出る。

なお、これを一番簡単に回避する方法は、以下のように cstdalign をコメントアウトして

#include <cstddef>
#include <limits>
#include <climits>
#include <cfloat>
#include <cstdint>
#include <cstdlib>
#include <new>
#include <typeinfo>
#include <exception>
#include <initializer_list>
//#include <cstdalign>
#include <stdexcept>
#include <cassert>
#include <cerrno>
#include <system_error>
#include <string>
#if __has_include(<string_view>)
# include <string_view>
#endif
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <stack>
#include <iterator>
#include <algorithm>
#include <cfenv>
#include <random>
#include <numeric>
#include <cmath>
#include <iosfwd>
#include <iostream>
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#if __has_include(<filesystem>)
# include <filesystem>
#endif
#include <cstdio>
#include <cinttypes>
#include <regex>
#include <atomic>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <future>
using namespace std::literals ;

これをコンパイル。

g++ -std=c++17 -include all.h -o hello hello.cpp

などとする方法だろう。

こうすれば、mac でもとりあえずは動く。

 

しかし、この本、冒頭で「オススメ」とか書いちゃったが、今、読み返してみると、これは初学者向けの本ではないべ。

関数の説明でいきなりラムダ式

例えば、「関数」の説明でいきなりラムダ式が出てくるw

int main(){

    auto print = [](auto x){
        std::cout << x << "\n";
    };

    print("hello");
}

いや、手順通りにやれば確かに hello とは表示されるんですけどね。

あとで、これは本物の関数ではないと断っているんですが、お戯れ感がすごい。

実は class という言葉はなかなか出てこない

ほとんど struct で通しているw

というか今読み返している箇所では、今のところ class 出てきてねー(笑)。

C++ では関数も struct で扱えるからこれでも大した差は出ないんだが、

class のデフォルトでのアクセシビリティはデフォルトで private

struct のデフォルトでのアクセシビリティはデフォルトで public

という違いはある。
(『C++ における class と struct の違い』あたり参照)

実用的にはこれでも問題ないと思うが、多くの初学者が思う

「オブジェクト指向の特徴の一つ=カプセル化」

概念からすると違和感感じるかもしれない。

ここら辺はいわゆるストラウストラップ本の「C++ の言語機能の中核は、クラス(class)である」のような説明と一線を画す。
実際、ストラウストラップ本では、まず complex というクラス(名前から予想がつくように複素数を取り扱うクラス)の説明から入っている。

なお、たまに誤解している人もいるが struct でも継承はできます。

だから、「オブジェクト指向なので class というキーワードや使い方を期待している読者もいるかもしれないが、実用的には struct で代用できるので、ここでは struct で説明していく」と一言断っておけばいいんですよね。

また、Objective-C でも class は出てきません(@class というディレクティブはあるが、Java のようにわかりやすい形で class という単語が使われているわけではない)。NSObject を継承していて @property でメソッドが定義されているものがクラスだ、という風に理解されていると思います。

何をもってオブジェクト指向というのか?という宗教論争を引き起こしかねない話をここでする気はないので、まあ、そこら辺はスルーします。

テンプレートの説明はわかりやすい

逆にテンプレートのサンプルはわかりやすかったりする。

template <typename T>
T twice(T n){
    return n * 2;
}

int main(){
    twice(3);
    twice(3.14);
}

ああ、なるほど、こう書けば、いちいち型を気にする必要はありませんね。

プリプロセッサ

ここもわかりやすい。例えば

_cplusplus 具体的には C++17 ならば 201703L

あたりとか(40.8)。

なんというか書くのが疲れてきた(と思われる)後半に入って、わかりやすい記載になっていくのが興味深い。

ポインタ

著者が自信を持っていうだけあって、わかりやすくはあると思う。

ただ、これも初学者にはどうか?

スマートポインタ

ここもある程度慣れた人にとってはわかりやすいと思う。

スマートポインタ→生ポインタ (36章)あたりは、「へー、そういうことだったんですか!」といたく納得した。

 

(続く)

「プロになる Java」を読んでみる

けっこう評判がいいようなので『プロになる Java』という本を読んでみた。

悪い本ではないが、これでプロになれるかというと「あれ?」という感じ。

どちらかといえば初学者向けの本だと思う。あるいは(Java8)以前に Java をかじったことはあるが、最近触ってない人向け。

内容も

文法
→swing を使ったデスクトップアプリのサンプル
→spring によるウェブアプリのサンプル

という風に進んでいくので、どちらかといえば教科書的実習書みたいな構成になっている。

一昔前に栄華を放った Java8 と現在標準になっている Java17 では、文法面で変更・追加されたところがあるので、そこらへんのチェックにはいいと思う。
Java17 に対応した網羅的な教科書はほとんどないので。

都市部でフリーランス

ところで「秋葉ちゃん」アカの中の人は複数人いる(複数人による共同管理。ただし、記事書いたりするのは特定の人に限られる。まあ慣れた人が書くのが一番)。
比較的若い年代が多いと思うが、関与したプロジェクト数はけっこうあると思う。

で、ここらへんのキャリアの人が思い浮かべる「プロ」というのは

プロジェクトにフリーで参加して
それなりにアウトプットを出し
トラブルなく対価もらえる人

が定義になるだろうか。

実際、これができれば、都市圏なら食うには困らない。

その一方で、プロジェクトの途中で失踪する人、メンタル病んで戦線離脱する人・・・も数多く見てきたw

その差はどこで生まれるのだろうか?

生き残れる人とそうでない人の差は、プログラミングに関する知識というよりは「ロジックを生み出せるかどうか」によるんではないかというのがワシらのおおよその見解だ。

(こちらは了)

延長戦的雑談

ところで、この書評?は、本意とは全然違うのだが、最後の方の業界話の方が評判はいいようだ。

というわけで、続きを書こう。

確かに、あまり能力に恵まれているとはいえないが、それでもこの業界にしがみつくしかない人たち、というのは一定数いる。

個人的には(特に意識してやっているわけではないが)ネット上でそういった人たちの言動などをチェックしたりもしている。たぶん、ここここで取り上げられている人たちの一人だと思うんだが、そういった監視の対象になっている人もいる。
ここでは、特定されると嫌なので、そういった人たち複数人の特徴を混ぜて書く。

なんでそんなことをするかといえば、理由は、単純にその言動が馬鹿馬鹿しくて興味深いから。
それと、今すぐというわけではないが、将来的に、そういった人たちが絡んできた時にうまくかわしたいから、というのもある。

まず、こういった一種の炎上商法に加担しているような人たちだから、この業界でやっていくための本質的に何かが欠けている。

なのだが、体面を保つためなのか、すごく自慢げに「こんなことできました」、「今は、これをやっている」、「だから、俺、スゴいでしょ」的な発信がうざいくらい多い。

けれど、よく読むと、出来合のソフトをインスコしましたってだけなんだよね(笑)。

どこがスゴいのだろうか?

そんなんだから、誰も注目していない。

閲覧数やシェア数が悲惨なまでに少ない。
閲覧が 20 にも満たない時がほとんどで、悪い時は一桁。
たぶん、本人しか見ていないwww

通常の感覚なら、ネット上での活動を止めるか抑え気味にして、リアルでの活動を頑張るところだろうが、本人は一向に止める気配がない。

リアルで承認欲求が満たされないから、ネットで、というよくある話なんだろうが、それだけでは収まらない部分もある。

他罰的な性格傾向

他者からの認められたいと思う時、健康な職業人ならば「他人ができないような+α を出す」とか自分を高めるように努力するものだと思うが、もちろん、こういった人たちはそんな努力はしない。

想像はつくとは思うが「周囲を下げる」といういかにもな行動をとる。しかも過剰なまでに。

また、こういう行動パターンが続けていることから分かるように、地方在住者が多い。
ちなみに都市部だと、まず、この行動パターンは取れないと思う。下げてしまった「周囲」からの直接的な反発は当然あるし、プロジェクトのリーダークラスがこういった人をメンバーから外すので、仕事が途切れて淘汰されてしまうから。

 

(こちらは続く)

 

ものぐさ Jersey

Java の EE 環境は JakartaEE に移行しつつあるが、当然、EE の各仕様もアップデートされている。
仕様すべての特徴を理解するのは面倒だが、使う機能は限られている。
おそらく JAX-RS や JPA, CDI あたりが基本で、それらの仕様の概略が頭に入ってれば、実務的にはそうは困らないと思う。

で、JAX-RS の話。

JAX-RS は仕様なので、実際に使う際にはこれを参照した実装が必要。

実際のアプリケーションサーバーでは

WildFly・・・RestEasy

GlassFish, Payara・・・Jersey

がモジュールとして組み込まれている。

前回、Payara を取り上げたので、今回は Jersey。

現在(2022冬)の最新版は 3.1.0 だそうで、そのユーザー向けドキュメントがここにある。

気がついたことなどをいくつか。

クライアント

クライアントでの使い方。

リクエストを投げる時は Invocation で

Client client = ClientBuilder.newClient(); //client の作成

だとか

WebTarget target = client.target(uri).path(path); //target の作成

あたりは ReastEasy とさほど違わないが、実際にサーバーにリクエストを投げる時は

Invocation.Builder builder = target.request(); //リクエスト

と builder を作成してから

buider.get(); // GET メソッドの場合
buider.post(Entity.entity({json のデータ}, MediaType.APPLICATION_JSON)); // POST メソッドの場合

などとするらしい。

また、レスポンスを取り出すときは

Response response = builder.get(); //レスポンス

とするらしい。

これは知らないと使えない。

レスポンスから中身を取り出す

レスポンスから中身を取り出したいときは、多分、色々なやり方があると思うが、いきなオブジェクトマッパーを使ったりすると初心者は混乱すると思うので、まずは String で取り出すのがいいと思う。

result (String です) = response.getEntity(String.class); //なんと1行

と実に簡潔に実現できる。
あるいは、状況によっては

result (String です) = response.readEntity(String.class); //なんと1行

となる。

プロジェクト作成初期なぞ、サーバーと通信できているかどうか不安になると思うので、まずは、こういったコードで確認するのがいいと思う。
まあ、日本語は文字化けはすると思いますが。。。

JSON とか Jackson とか

一歩進む。

JSON を使う

この記事が参考になるかな。

pom.xml に何か加えよ、とあるが、これおそらく Jersey を GlassFish あたりで使う場合。
WildFly(27) だとこの指定は全く不要。
同様のコードをサーバープログラムに書いて、WildFly で稼働させ、 curl でアクセスしたら以下のように普通に動いた。

まあ、ただ、Response で返す、というのは、知識の定着によき。

Jackson JSON を使う

Jersey 公式がサンプルを提示してくれている。

しかし、これわかりにくいなあ。

Jersey や RestEasy を使うのは、特定のクラスのインスタンスを(JSON 形式で)クライアント・サーバー間でやり取りしたいってときなので、ドンピシャのサンプルという感じがしない。

RestEasy はもっと簡単だったような気がするが、はて?

→ やはり、典型的なオブジェクトをやり取りするだけだったら、変に複雑にする必要はなさそうです。

例えば、サーバーサイドから User.class の JSON 形式のオブジェクト

{"id":"123","name":"akiba taro"}

を返し、クライアントで受け取るなら、単に

user = response.readEntity(User.class);

とすればいいだけです。
もちろん、サーバー側では User で返す必要がありますが。

ところで Jackson とは?

Java 用の JSON パーサーライブラリの1つ。Java オブジェクトと JSON の相互変換とかができる。

みたいに説明されている。 まあ、そんなところでしょう。

 

 

Payara 6 を使う

WildFly, TomEE ,GlassFish ときたので、ここは当然? payara にも手を出してみる。

インストール

公式ページのここから適当なバージョンの Payara をダウンロード。
今だと JakartaEE 10 対応の Ver6 系列が落とせるので、そいつの full の方をチョイス。
web profile もあるが、違いを解説できるほど詳しくはないので今回はスルー。

なお、環境は MacOS Monterey, java17 ですが、windows 環境でも同様の手順で起動〜簡単な動作確認はできるでしょう。

解凍すれば使えるようにはなっている。

問題は置き場所だが、多分、ほとんどの人がまずはお試しで使うと思うので、/usr/local ではなく、ユーザーホームの適当なフォルダに配置すればいいと思う。

/payara6/bin

に移動して

./asadmin start-domain

でまずは起動。

ブラウザで localhost:4848 にアクセスして、以下のような管理コンソール画面が表示されればまずは Payara のインストール自体は成功しています。

なんらかの事情でインストールした(ローカル)ホスト以外のマシンからアクセスするためにはパスワード設定を有効にする必要があります。

./asadmin change-admin-password

を実行して新しい管理者とそのパスワードを設定。その後

./asadmin enable-secure-admin

を実行して、再起動すれば、外部リモートマシンから管理コンソール画面にアクセスできるようになります。

上記設定が完了すると管理画面呼び出し時にパスワード入力を求められるようになります。

 

とりあえず簡単な war ファイルをデプロイしたいんだが、その前に・・・

Payara の port を変更

8080 ポートは、WildFly や Tomcat などで使用されている場合が多いと思うので、あらかじめ 80 に変更しておきたい。

これは上のコンソール画面から簡単に行える。

画面右のノードから configurations -> server-config -> Network Config -> Network Listners の順に展開。

下図の Port: 8080 のところを 80 に変更。

glassfish と同様にその場で変更できる。再起動不要。

実際、この状態で localhost:80 にアクセスすると

というウェブページが表示される。

ここらへん、Tomcat あたりと違う。

データベース(PotgreSQL)との連携

GlassFish の時と同様、

JDBC コネクションプールの作成→データリソースの作成

とすればいいです。

ただし、GlassFish の時と違うのはコネクションプールの作成より先に jdbc ドライバをコマンドラインから登録しておくこと。

Payara 起動後、コマンドラインから

asadmin add-library (path)/postgresql-(version).jar

と入力。

Command add-library executed successfully

と返ってくれば OK牧場です。

よくわからなければ公式のブログ記事(『Using PostgreSQL with Payara Server』)参照。

コネクションプールを作成して、ping を打った時に

と Ping Succeededとなれば、設定はうまくいってます。

GlassFish の時と同様、クラスパスの設定などは不要です。
設定がうまくいってないと「クラスパスを正しくセットしる」みたいなエラーメッセージは出るんですが、真にうけない方がいいでしょう。

簡単なウェブアプリで動作確認

今回も jpa-hibernate プロジェクトを使用。

なんですが、通常の書き方だと動作しません。

しばらくハマって頭上に?マークを浮かべてたんですが、これは Payara6 で hibernate を使う時に出現するバグのようです。

GitHub isuues でスレ立ってます。

解決方法は、そこにも書いてありますが、persistence.xml に

 (persitence.xml)
            <property name="hibernate.type.xml_format_mapper" value="jaxb"/>
            <property name="hibernate.type.json_format_mapper" value="jsonb"/>

の2行を追加することです。
しかし、この解決(最終的な解決ではないですが)方法が提示されたのは、この記事書いている4日前。

当方の JakartaEE 力もあがってきているなあと思うと同時に、自由を維持していくことはそれなりの労力がかかるんだということを痛感させられます。

バグについて

なお、このバグに興味を持って、Payara のソースコードを少し読んだんだが、内部的には JavaEE 8 的な処理があって、これはちょっと根が深いかな?とは思った。

WildFly の鬼のような分量のソースコードを読むのに比べれば、負担は少ないのかもしれないが、これに取り組むのは気が引ける。。。

バグ?

データベース名に database-1 などとハイフンを入れるとハイフン以下を無視して動作するようだ。

 

 

 

CDI と weld の関係

Java の習得は、SE → EE の順に進むと思うが、EE になって役者がごそっと出てくるため、躓きやすい。

これまでにも EJBJPA の話題は取り上げてきたが、今回は CDI 。

まず、CDI と weld の関係だが、CDI が仕様で weld がその実装の一つ。

WildFly の stanalone.xml などで

        <extension module="org.jboss.as.weld"/>

とあるのは、この WildFly には weld を組み込んでいますよ、という意味だ。

WildFly 27 では、weld 5.1 が採用されているので、その概略は

Weld 5.1.0.Final – CDI Reference Implementation

でチェックするといいだろう。

下手な日本語解説記事よりわかりやすい。

CDI とは?

ところで CDI とは何だろう?
よく依存性注入がどうしたこうしたと難しげに解説してあるサイトがあるが、初学者が理解できるとは思えない。
簡単に言えば、あるオブジェクトを使うのにいちいち new して作成せずともコンテナの方で勝手にやってくれる機能のことだ。

注入されるクラスにはそのインスタンスの生存期間を明示する必要があるので scope 関係のアノテーションを付与し、実際に注入する際には @Inject アノテーションでその箇所を明記する。

なお、CDI は Contexts and Dependency Injection の頭文字をつなげたもの。

サンプルコード解説

いつものように簡単なサンプル作成。

インジェクトされるクラスとインジェクトするクラス(サーブレット)を作成し、使用通りに動くか検証。

インジェクトされる側のクラスは以下の通り。

ScopedSevice.java
------
@RequestScoped
public class ScopedService {

    private int counter = 0;

    public void countUp() {
        this.counter++;
    }

    public int getCounter() {
        return this.counter;
    }
}

これを以下のサーブレットクラスにインジェクトする(今回はフィールドにそのままインジェクト)する。

CDI.java
-------
public class CDI extends HttpServlet {

    @Inject
    ScopedService scopedService;

    @Override
    protected void doGet(final HttpServletRequest pReq, final HttpServletResponse pResp) throws ServletException, IOException {
        this.scopedService.countUp();
        this.scopedService.countUp();
        pResp.setContentType("text/plain");
        pResp.getWriter().println("Counter Value -> " + this.scopedService.getCounter());
    }
}

ScopedService クラスは new などで明示的にインスタンス化されていないが、CDI クラスから問題なく使えるか試すコードです。
インジェクトされた後、countUp メソッドを2回呼び出しているので、counter = 2 となっていれば、良いわけです。
サーブレットなので、ブラウザから呼び出すと

と見事に予想通りの処理をしてくれています。

ソースコードは github にあげてあります。

面白かったのは、Inject される側のクラスで @Singleton を使うと以下のようなエラーが出た点だ。

Dispatcher error: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"CDI-Jakarta10-1.0.war\".WeldStartService" => "Failed to start service
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type ScopedService with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject servlet.CDI.scopedService
at servlet.CDI.scopedService(CDI.java:0)
"}}

CDI を使う利点の一つは、スコープの管理が楽になることなので、安直に singleton は使わない方がいいのかもしれません。

(追記)ここ、ワイ、勘違いしてたかも。
@Statless, @Stateful, @Singleton は EJB の文脈で用いられるアノテーションなので、単純に CDI では使えないってことなのかも。

参考

ここ

Unfortunately, there’s a little problem with this pseudo-scope. Beans with scope @Singleton don’t have a proxy object.

CDI の仕様が変われば、うまく扱えなくなること確実なアノテーション。それが @Singleton www