Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

Seasar2の環境構築&動作確認のメモ(Java8)

f:id:fa11enprince:20200628230744j:plain Seasar2によるサンプルアプリを作成してみます。
今回はDoltengを使います。
Java8にSeasar2は対応していない部分があるので最初のDoltengとs2jdbc-gen-buildを使うときだけ Java7で進めます。 最後にJava8にします(その代わり以降Doltengが使えなくなります)。

1.前準備

JDKのインストール(Java8対応)

http://www.oracle.com/technetwork/java/javase/downloads/index.html
あたりからJava8(JDK8)のをインストールします

Eclipseのインストール

http://mergedoc.osdn.jp/ eclipse 4.5 Marsを推奨します。 Cドライブ直下に移動して解凍します。
eclipseというフォルダができています。

Tomcaのインストール

不要です。Eclipse付属のを使います。

もし別途Tomcatをインストールしたい場合は、 お好きなTomcatのバージョンのもの(Tomcat8あたりがおすすめ)をインストールしましょう。
その際mysql-connector-javaをTomcatのlib下に置き忘れないようにしましょう。
Tomcat8のディレクトリにmysql-connectorを置きます。
https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.39
mysql-connector-java-5.1.39
をダウンロードして
C:\Program Files\Apache Software Foundation\Tomcat 8.0\lib
に置きます。

MySQLのインストール

バージョンは何でもよいのですがここからダウンロードしましょう!
http://dev.mysql.com/downloads/mysql/
versionは5.7が良いと思います。

MySQLのユーザ作成

コマンドプロンプトなどを立ち上げて

> mysql -uroot -p
> GRANT ALL PRIVILEGES ON *.* TO appuser@localhost IDENTIFIED BY 'password';
> SELECT Host, User FROM mysql.user;

を実行します。

Seasar2 pluginを入れる

以降の説明で基本的に英語表記のまま書きますが、きっと日本語環境だとあれれ?と思うところもあると思うので()内に日本語表記を併記します。

eclipseを立ち上げます。
Welcomeページが出ますが、無視して閉じます。
Help(ヘルプ) > New Install Software(新規ソフトウェアのインストール)から
Addボタンを押して

Name(名前): Seasar2 
URL(ロケーション): http://eclipse.seasar.org/updates/3.3/

でOKを押します。

Kijimuna
Resourcesynchronizer
S2JUnit4Plugin
SAStrutuPlugin

を選びNextを押します。あとは指示に従いインストールします。
最後にeclipseの再起動があります。

Seasar2アプリを作成

プロジェクトのセットアップ

Eclipseを立ち上げて 右クリック > New(新規作成) > Project(プロジェクト) > Dolteng Project とします。 次にプロジェクト名とルートパッケージ名ですが何でもよいです。

Project Name(プロジェクト名): myseasar2
Root Package Name(ルートパッケージ名): myseasar2

としておきましょう。

JRE Container(JREコンテナー)

Use an execution environment JRE(実行環境JREの使用): JavaSE-1.7  

JavaSE-1.8だとダメです。

Project Facet Settings (プロジェクトファセット設定)

Presentation: SAStruts
Persistence: S2JDBC
Server Management: WTP(Servlet 2.5)

を選んでFinish(完了)を押します。 そうするとすでにひな形が完成しています。

MySQL 関連する設定ファイルの書換え

Eclipseのworkspace\myseasar2001\src\resoruces\dataにある
demo.sqlを開きます。
最初の2行はMySQLでは通用しないので消して次の3行を追加します。

USE TEST;
CREATE TABLE EMP(ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, EMP_NO INTEGER NOT NULL ,EMP_NAME VARCHAR(20),MGR_ID INTEGER,HIREDATE DATE,SAL NUMERIC(7,2),DEPT_ID INTEGER, VERSION_NO INTEGER);
CREATE TABLE DEPT(ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, DEPT_NO INTEGER NOT NULL,DEPT_NAME VARCHAR(20),LOC VARCHAR(20), VERSION_NO INTEGER);

MySQLワークベンチを立ち上げてそのSQLを実行します。

次に src\main\resources\jdbc.diconを書き換えます。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
    "http://www.seasar.org/dtd/components24.dtd">
<components namespace="jdbc">
    <include path="jta.dicon" />
    <!-- ここから -->
    <!-- MySQL -->
    <component name="xaDataSource"
        class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
        <property name="driverClassName">
            "com.mysql.jdbc.Driver"
        </property>
        <property name="URL">
            "jdbc:mysql://localhost:3306/test?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTC"
        </property>
        <property name="user">"appuser"</property>
        <property name="password">"password"</property>
    </component>
    <!-- ここまで -->

    <component name="connectionPool"
        class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl">
        <property name="timeout">600</property>
        <property name="maxPoolSize">10</property>
        <property name="allowLocalTx">true</property>
        <destroyMethod name="close" />
    </component>

    <component name="DataSource"
        class="org.seasar.extension.dbcp.impl.DataSourceImpl" />

</components>

test?以降がないと環境によっては

java.sql.SQLException: The server timezone value 'UTC' is unrecognized or represents more than one timezone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc timezone value if you want to utilize timezone support.

のようなエラーが出ます 別の回避策があるかもしれません。

src\main\resources\s2jdbc.diconも書き換えます。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="jdbc.dicon" />
    <include path="s2jdbc-internal.dicon" />
    <component name="jdbcManager"
        class="org.seasar.extension.jdbc.manager.JdbcManagerImpl">
        <property name="maxRows">0</property>
        <property name="fetchSize">0</property>
        <property name="queryTimeout">0</property>
        <property name="dialect">mysqlDialect</property>
    </component>
</components>

pom.xmlの設定を変更します

mysql-connector-javaのバージョン6以上だと動作しないものがあります。 そのため5.1.39にしておきます。 さらにproject直下にあるpom.xmlを書き換えます

        <!--
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.0.69</version>
        </dependency>
        -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.39</version>
        </dependency>

さらに問題のあるjavassistを変えておきます(Java8対応)

s2-frameworkが古いバージョンのjavassistに依存しているのでexclusionsを指定します。

        <dependency>
            <groupId>org.seasar.container</groupId>
            <artifactId>s2-framework</artifactId>
            <version>2.4.46</version>
            <!-- 追加 -->
            <exclusions>
                <exclusion>
                    <artifactId>javassist</artifactId>
                    <groupId>jboss</groupId>
                </exclusion>
            </exclusions>
            <!-- 追加 -->
        </dependency>
...
        <!--
        <dependency>
            <groupId>jboss</groupId>
            <artifactId>javassist</artifactId>
            <version>3.4.ga</version>
        </dependency>
        -->
        <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.22.0-CR1</version>
        </dependency>

これを変えておかないと 500 - java.lang.RuntimeException: java.io.IOException: invalid constant type: 18 というエラーがでます。 ポイントは2つ s2-frameworkから古いjavassistをexclusionの指定。 また古いjavassistを新しいバージョンにします。

s2jdbc-gen-build.xmlの実行

mysql-connector-javaのVersionが6以上だとs2jdbc-gen-build.xmlが動きません しかし今は1.7にしてあるので大丈夫です。

Maven Repository: mysql » mysql-connector-java » 5.1.39 からmysql-connector-javaをダウンロードして mysql-connector-java-5.1.39をプロジェクトのlib下に置いて(エクスプローラーからコピー&Eclipse上でペーストでOKです)
lib下をリフレッシュ
プロジェクトトップで右クリック > Java Build Path(ビルドパス) > Libraries(ライブラリー)からJARの追加をする。
これをしないとビルドパスが通ってないので次に行うant buildが失敗します。

s2jdbc-gen-build.xml
の1行目
gen-ddlをgen-entityに変えて実行します。
s2jdbc-gen-build.xml

<project name="myseasar2-s2jdbc-gen" default="gen-entity" basedir=".">

右クリック Run As(実行) > External Tools Configurations(外部ツールの構成)
ダイアログの左側のAntビルドをダブルクリック
Target(ターゲット)タブでgen-entityになっているのを確認
Common(共通)タブでEncodingでUTF-8
JREタブでここだけJava7にします
Runを押します。

正常に実行できればクラスなどが自動生成されています。

備考

Java6だとエラーが発生します。
また、mysql-connector-javaのVersionが6以上だとs2jdbc-gen-build.xmlが動かない
mysql - mybatis generator "Column name pattern can not be NULL or empty" - Stack Overflow

Mavenのビルドをします

右クリック > 実行 > maven install
を実行します。
実行できない場合はMavenプロジェクトになっているかどうか確認してください。

Tomcatの設定

Server View(サーバー)でサーバーを新規作成します。
右クリック > 新規作成
Tomcat v8.0 Serverを選択しNextを押します。
サーバ・ランタイム環境はJava8でよいです
Add and Removeで
myseasar2があるので追加します。
Finishを押すとサーバーが作成されます。

実行します

Server ViewからStartを押します。
http://localhost:8080/myseasar2/
と押すとHello worldと出ていると思います。

Scaffoldを利用します

Java7じゃないとできません Window(ウィンドウ) > View(ビューの表示) > Dolteng > Database View(データベースビュー)
を出します。 Database Viewでs2jdbc.diconをダブルクリックすると DeptとEmpが出てくるのでそれぞれを右クリックして
Generate Scaffold Application(Scaffold アプリケーションの作成)を押します。 プロジェクトをリフレッシュするとviewなどが自動生成されています。

ソースコードを書き換えます。

src/main/webapp/WEB-INF/view/index.jsp
を書き換えます。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>トップページ</title>
</head>
<body>
<s:link href="dept">dept</s:link>
<s:link href="emp">emp</s:link>
</body>
</html>

再びブラウザを立ち上げて実行します。

リロードしましょう http://localhost:8080/myseasar2/

Java8化します

サーバを停止し、
プロジェクトを右クリック > Javaコンパイラー
Javaビルドパス上の実行環境JavaSE-1.7から準拠を使用のチェックを外し、コンパイラー準拠レベルを1.8にします。
適用を押します。
プロジェクト・ファセットを選び
Javaの部分の1.7を1.8に変更します。

以上で終わりです。

その他

なんかいろいろいじってたらサーバが立ち上がらない

java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component
というエラーが出る 過去の設定のごみが残っている可能性が高いので、手っ取り早いのはサーバービューから
新規にサーバを作成してモジュールを追加して実行する

それが嫌な場合はモジュールを除去して保存して消す。
追加しなおしてtomcatワークディレクトリをクリーン&通常のクリーン をする。

Java8にしたらエラーが…

Java8にしただけでは起きずJava8の特有の機能を使ったときに発生します。
500 - java.lang.RuntimeException: java.io.IOException: invalid constant type: 18
これが出た場合はjavassistを入れ替えましょう。対応方法はこのページに書いています。
また、これを対応した後古いjavassistが残っちゃってるのでクリーンしましょう。
私が対応したときは上記のようにごみが残っていたのでeclipse上のサーバーを作り直しました。

SAStrutsの環境構築とチュートリアルのセットアップ


今更ですが、SAStrutsのtutorialの環境構築をします。
SAStrutsとはJavaのWebフレームワークStrutsの1系を改造したものです。

公式のチュートリアルのセットアップのページの手順を踏むと
情報が古いのか不親切なのかたぶんうまくいかないでしょう。
下記ではスムーズにいっていますが、実はいろいろ苦戦しました。
たぶんほかの人も同じようなことではまるんじゃないでしょうか?

  • Eclipseの解凍がうまくいかない(実はこれが一番ハマった...)
  • Tomcatのバージョンの指定がうまくできない(7以上を使いたいのに6までしか表示がないなど)→チュートリアル通りにやった場合そうなる。
  • インポートしたプロジェクトが動的プロジェクトになってない
  • http://localhost:8080/sa-struts-tutorialにアクセスすると404 Not Foundになる

今回の環境構成

OS Windows 10(Windowsなら何でもよい)
Eclipse Pleiades all in One 4.5 Mars (Full Edition)
JRE Java7 (Pleiades付属)
Tomcat 8.0.32 (Pleiades付属)

注意事項としてSAStrutsのセットアップに説明のある
Tomcat LauncherとTomcat本体は別途インストールしません(Eclipseに付属のため)。
あと、プロパティエディタも確かもともとついているはずなので無視します。
※Java8じゃなくてJava7にしているのはJava8だと追加の設定が必要なのと一部機能が使えなくなってしまうからです(Scaffoldなど)

1.EclipseのPleiades all in oneをダウンロード

https://mergedoc.osdn.jp/
にアクセスして
Eclipse 4.5 Mars Java 64bit Full Editionをダウンロード。
(32bit環境の方は以降32bitに読み替えてください。)
Add Block等を入れている方はOFFにしましょう。
また、Google Chromeの場合、ダウンロード時にURLの横に盾マークがでて
このページは承認されていないソースからスクリプトを読み込もうとしています。
とでるので「安全でないスクリプトを読み込む」を押しましょう。
1GBほどあります。
Chromeを使っているとありがちですが、わりとダウンロードが失敗します。
ダウンロードした後の容量が1GB以上あるか確認しましょう。
解凍したらeclipse.exeがないなんてことがありました。

2.EclipseのPleiadesをインストール

ダウンロードしたファイル(私と同じものを選んだ場合pleiades-e4.5-java-jre_20160312.zip)
を解凍します。
なぜかWindows標準で備わっている解凍ツール以外で解凍するとファイルが壊れます。
解凍した後なぜかeclipse.exeが0バイトになっているなんてことがあります。

公式サイトに下記の記述があります。

解凍ツールは Windows 標準の展開ウィザードまたは WinRAR を推奨します。
この2つのツールは解凍時に MAX_PATH (最大パスを示す Windows 定数 = 260 文字) を超えていた場合、
適切なエラーを返します。その場合は c:\pleiades のような短いパスに解凍してください。
Windows XP 展開ウィザード、Lhaz、Explzh は MAX_PATH を超えている場合、
書庫が壊れているかのようなエラーが出ますが、
これも短いパスに解凍することで対応可能です。
7-Zip、Lhaplus、Lhaca は使用しないでください。MAX_PATH を超えていても警告やエラーが表示されず、
正常に解凍できたか判断できません。 

なので次の手順を行います。
pleiades-e4.5-java-jre_20160312.zipをC:\直下に置きます。
右クリック→プログラムから開く→エクスプローラー
で開き、Exploer上に出ているすべて展開ボタンを押します。
展開先の選択とファイルの展開ダイアログで、
C:\pleiades-4.5
としておきましょう。
※私の環境にはpleiades-4.4もあるため。
そうするとC:\pleiades-4.5\pleiadesとなっているので
1階層あげて
C:\pleiades-4.5としておきましょう。
これでこうなるはずです。
C:\pleiades-4.5の下には

.metadata.default
eclipse
java
tomcat

の4フォルダがあるはずです。

C:\pleiades-4.5\eclipse
を開いてeclipse.exeを右クリックしてタスクバーにピン止めかショートカットを作成しておきましょう。

3.チュートリアル本体のダウンロード

http://sastruts.seasar.org/download.html
にアクセスして、
Super Agile Strutsチュートリアル
sa-struts-tutorial-1.0.4-sp9.zip
をダウンロードしましょう

4.SAStrutsでの開発を便利にするPluginをEclipseにインストール

Eclipseを起動します。
ヘルプ > 新規ソフトウェアのインストールで追加ボタンを押します。
名前: Seasar Plugin3.3
ロケーション: http://eclipse.seasar.org/updates/3.3/
でOKを押します。
しばらくすると一覧が表示されるので
Dolteng
EMecha
Kijimuna
matatabi
ResourceSynchronizer
S2Junit4 Plugin
SAStrutsPlugin
を選び次へを押します。
Ymir以外を選びます。
あとはウィザードに従い完了を押します。

5.チュートリアルをインポートします。

Eclipseからファイル > インポートで
一般 > 既存プロジェクトをワークスペースへ
次へを押します。
アーカイブファイルの選択でsa-struts-tutorial-1.0.4-sp9.zipを選択します。
完了を押します。
たぶん、このあとエラーが出ますが気にしないでください。
エラー例:ビルドパスのエラーが解決されるまで、プロジェクトをビルドできません。

6.チュートリアルを動的プロジェクトに変換します。

ここの作業が重要です。ここのセクションのことを実施しないと
たぶん404 Not Foundを見続けることになります。
プロジェクトを右クリック > プロパティー
Tomcatを選択します。
全般タブでTomcatプロジェクトにチェック
コンテキスト名をsa-struts-tutorial
にしてOKを押します。

また、プロジェクトを右クリック > 構成で
プロジェクト・ファセットの変換を選びます。
一度選ぶとプロパティーのほうに出てきます。
動的Webモジュールにチェックを入れます。
ランタイムタブを選んで、
Tomcat8を選択

また、プロジェクトを右クリック > プロパティーで
デプロイメント・アセンブリーを選択します。
/WebContent /というのがあるので除去します。

追加ボタンよりフォルダーを選択します。
/src/main/webapp でデプロイ先を / にします。

この辺はここを参考にしました。
http://knightsheep.blogspot.jp/2014/01/web-eclipseweb.html

7.Eclipse付属のTomcat8の設定をします。

ウィンドウ > 設定でTomcatを選びます。
Tomcatホーム C:\pleiades-4.5\tomcat\8
コンテキスト・ファイルを選んで
コンテキスト・ディレクトリー C:\pleiades-4.5\tomcat\8\conf\Catalina\localhost
とします。
するとエラーが消えるかと思います。

8.コンテキスト定義を更新します。

プロジェクトを右クリック > Tomcatプロジェクト
コンテキスト定義を更新を実行します。

9.実行します。

プロジェクトを右クリック > 実行 > サーバーで実行を押します。
Tomcat v8.0サーバを選択します。
サーバのホスト名はlocalhostのままでよいです。
毎回コレで実行のチェックをしておいたほうが便利です。
完了を押します。
しばらくするとTomcatが起動し、
Eclipse上のブラウザが起動し、画面が表示されるはずです。

パースペクティブを開くからJavaEEを追加してそれに切り替えたほうが見やすいと思います…。

C++でカレンダー

f:id:fa11enprince:20200628230502j:plain
カレンダーなんてJavaScriptを使ったらすでにたくさん便利なのがありますが、 自分でカレンダーを書いたらどうなるか…とおもい書いてみました。C++で。 そういえばツェラーの公式というのがあり、使ってみました。 C++はここ数か月書いてませんでしたが以外に覚えているものですね。 std::unordered_mapをconstにしたらmap[i]みたいのができないんだメンドクセ…っていうのがありました(findで代用しました)

ソース

#include <cstdio>
#include <unordered_map>

namespace Pudding {
    using namespace std;
    static const unordered_map<int, int> DaysOfMonth = {
        {1,31}, {2,28}, {3,31}, {4,30}, {5,31}, {6,30},
        {7,31}, {8,31}, {9,30}, {10,31}, {11,30}, {12,31}
    };

    class Calendar {
    public:
        static void print(int year, int month) {
            printf("\n%4d%2d月", year, month);
            printf("\n 日 月 火 水 木 金 土\n");
            int index = get_day_of_week_by_zeller(year, month, 0);
            int position = index % 7;
            fill_with_blank_to(position);
            for (int i = 0; i < get_days_of_month(year, month); i++) {
                line_break_if_sunday(position);
                printf("%3d", i + 1);
                position++;
            }
            printf("\n");
        }

    private:
        // 指定位置まで空白を埋める
        static void fill_with_blank_to(int position) {
            for (int i = 0; i < position; i++) {
                printf("   ");
            }
        }
        // 位置により必要なら改行をする 7の倍数(この場合日曜)なら改行
        static void line_break_if_sunday(int position) {
            if ((position % 7) == 0) {
                printf("\n");
            }
        }

        // うるう年判定
        static bool is_leap_year(int year) {
            return (year % 400 == 0) ? true
                : (year % 100 == 0) ? false
                : (year % 4 == 0) ? true
                : false;
        }

        // 月の日数
        static int get_days_of_month(int year, int month) {
            int days = DaysOfMonth.find(month)->second;
            if (is_leap_year(year) && month == 2) {
                days = 29;
            }
            return days;
        }

        // ツェラーの公式 曜日のインデックスを求める (1:月~7:日)
        static int get_day_of_week_by_zeller(int year, int month, int day) {
            if (month < 3) {
                year--;
                month += 12;
            }
            return (year + year / 4 - year / 100 + year / 400 + (13 * month + 8 ) / 5 + day) % 7 + 1;
        }
    };

}

auto main() -> int {
    for (int i = 1; i <= 12; i++) {
        Pudding::Calendar::print(2016, i);
    }
}

実行結果

2016年 1月
 日 月 火 水 木 金 土
                 1  2
  3  4  5  6  7  8  9
 10 11 12 13 14 15 16
 17 18 19 20 21 22 23
 24 25 26 27 28 29 30
 31

2016年 2月
 日 月 火 水 木 金 土
     1  2  3  4  5  6
  7  8  9 10 11 12 13
 14 15 16 17 18 19 20
 21 22 23 24 25 26 27
 28 29

2016年 3月
 日 月 火 水 木 金 土
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29 30 31

...省略

単方向リスト(Singly Linked List)の実装 (C++)

f:id:fa11enprince:20160518180613j:plain
単方向リストを勉強がてら久々にビールを飲みながらC++で書いてみました。
これをやると、ポインタって何かってよく理解できますね。
しかしビールを飲むと、わけのわからないミスをしでかします…。

ソースコード

#include <iostream>
#include <exception>

namespace My {

    template <typename T>
    class SinglyLinkedList {
    private:
        struct Node {
            T value;
            Node *next;
            Node() : value(0), next(nullptr) {
            }
            ~Node() {
            }
        };
        Node *head_;
        int number_;

        Node *create(T value) {
            Node *node = new Node();
            node->value = value;
            node->next = nullptr;
            return node;
        }
        void destroy(Node *node) {
            delete node;
            node = nullptr;
        }
    public:
        SinglyLinkedList() :
            head_(nullptr)
            ,number_(0) {
        }
        ~SinglyLinkedList() {
            Node *cur = nullptr;
            for (Node *node = head_;
                 node != nullptr;
                 cur = node, node = node->next) {
                destroy(cur);
            }
        }
        void push_front(T value) {
            Node *new_node = create(value);
            if (head_ == nullptr) {
                head_ = new_node;
            }
            else {
                Node *node = head_;
                head_ = new_node;
                head_->next = node;
            }
            number_++;
        }
        void push_back(T value) {
            Node *new_node = create(value);
            if (head_ == nullptr) {
                head_ = new_node;
            }
            else {
                Node *node = head_;
                Node *tail = nullptr;
                for (; node != nullptr; tail = node, node = node->next);
                tail->next = new_node;
            }
            number_++;
        }
        void insert(int pos, T value) {
            Node *new_node = create(value);
            if (pos == 0) {
                push_front(value);
            }
            else if (pos < number_ - 1) {
                Node *prev = nullptr;
                Node *node = head_;
                for (int i = 0; i < pos; ++i) {
                    prev = node;
                    node = node->next;
                }
                prev->next = new_node;
                new_node->next = node;
                number_++;
            }
            else if (pos == number_ - 1) {
                push_back(value);
            }
            else {
                throw std::bad_exception();
            }
        }
        void pop_front() {
            Node *node = head_;
            head_ = node->next;
            destroy(node);
            number_--;
        }
        void pop_back() {
            Node *node = head_;
            Node *prev = nullptr;
            Node *tail = nullptr;
            for (; node != nullptr;
                 prev = tail, tail = node, node = node->next);
            prev->next = nullptr;
            destroy(tail);
            number_--;
        }
        void erase(int pos) {
            if (head_ == nullptr) {
                throw std::bad_exception();
            }
            Node *node = head_;
            if (pos == 0) {
                pop_front();
            }
            else if (pos < number_ - 1) {
                Node *prev = node;
                for (int i = 0; i < pos; ++i) {
                    prev = node;
                    node = node->next;
                }
                prev->next = node->next;
                destroy(node);
                number_--;
            }
            else if (pos == number_ - 1) {
                pop_back();
            }
            else {
                throw std::bad_exception();
            }
        }
        void display() {
            for (Node *node = head_; node != nullptr; node = node->next) {
                std::cout << node->value << ' ';
            }
            std::cout << std::endl;
        }
    };
}

auto main()->int {
    try {
        My::SinglyLinkedList<int> list;
        list.push_back(2);
        list.display();
        list.push_back(3);
        list.display();
        list.push_back(4);
        list.display();
        list.push_front(1);
        list.display();
        list.push_front(0);
        list.display();
        list.insert(1, 3);
        list.display();
        std::cout << "---------" << std::endl;
        list.pop_back();
        list.display();
        list.pop_front();
        list.display();
        list.erase(2);
        list.display();
        list.pop_back();
        list.display();
        std::cout << "---------" << std::endl;
        list.push_back(4);
        list.display();
    }
    catch (std::exception& e) {
        std::cout << e.what() << std::endl;
    }
}

イテレーターがねえ!とか
追加するたびnewしてる…とかいろいろ足りないところありますが、
勉強で書くにはこれで十分でしょう。
双方向リストじゃないと微妙に実用性ないですね。
ちょこっと変えると双方向リストになります。

実行結果

$ ./list.exe
2
2 3
2 3 4
1 2 3 4
0 1 2 3 4
0 3 1 2 3 4
---------
0 3 1 2 3
3 1 2 3
3 1 3
3 1
---------
3 1 4

参考リンクなど

以前にも書いてたな…