std::ostringstream を初期化して再利用する方法

  1. std::ostringstream::str で "" を引数に入れてバッファをクリア
  2. std::ostringstream::clear で内部状態をクリアリセットする
#include <sstream>

int main()
{
  std::ostringstream ss;

  // ostringstream にバッファを入れる
  ss << "hoge";

  // 1
  ss.str("");

  // 2
  // 引数のデフォルト値はstd::stringstream::goodbit)なので引数省略可
  ss.clear();

  
  
}

[Oracle Graph Server 20.4] PGQL CREATE 文で発行される SQL と PGQL を表示する方法

String query = String.format(
    "CREATE PROPERTY GRAPH %s " +
    " VERTEX TABLES(n1 LABEL hoge PROPERTIES(id, name, rate), n2 LABEL hoge2 PROPERTIES(id, name, rate)) " +
    " EDGE TABLES(e1 SOURCE KEY(n1_id) REFERENCES n1 DESTINATION KEY (n2_id) REFERENCES n2 LABEL el NO PROPERTIES)",
    graphName);
PgqlPreparedStatement pst = pgqlConn.prepareStatement(query);

// Create 文で発行される SQL,PL/SQL
PgqlSqlCreateTransImpl createTrans = (PgqlSqlCreateTransImpl) pst.translateStatement();
Object[] createTranslations = createTrans.getCreateTranslation();
Stream.of(createTranslations).filter(s -> s != null).forEach(s -> {
    switch(s.getClass().getName()){
        case "java.lang.String":
            System.out.println(s);
            break;
        case "[Ljava.lang.String;":
            Stream.of(((String[]) s)).filter(t -> t != null).forEach(System.out::println);
            break;
        default:
            System.out.println(s);
    }
});
{ call opg_apis.create_pg(graph_owner => :1, graph_name => :2, tbs_set => null, options => 'SKIP_INDEX=T') }
INSERT /*+ append */ INTO "TEST_USER".TEST_20_4_GRAPHVT$
(VID, VL, K, T, V, VN, VT)
SELECT VID, VL, K, T, V, VN, VT
FROM (
  SELECT 
    VID, VL, K, T, V, VN, VT
  FROM(
    WITH T0 AS (
      SELECT /*+ materialize */
        round(sys_op_combined_hash(n'"N1"' || '|' || TABLE_KEY)/2) AS VID
        , VL, "ID", "NAME", "RATE"
      FROM ( 
        SELECT 
          "ID" AS TABLE_KEY
          , n'HOGE' AS VL, "ID", "NAME", "RATE"
        FROM "TEST_USER"."N1"
      )
    )
    SELECT VID, VL, K, T, to_nchar(VN, 'TM9', 'NLS_Numeric_Characters=''.,''') AS V, VN, VT
    FROM (
      SELECT
        VID, VL, K,
          to_number(4) AS T,
          to_nchar(null) as V, to_number(property_value) as VN, to_timestamp_tz(null) VT
      FROM T0
      UNPIVOT (
        property_value FOR K IN (
          "ID" AS n'ID'
        )
      )
    )
  UNION ALL
    SELECT VID, VL, K, T, V, VN, VT
    FROM (
      SELECT
        VID, VL, K,
          to_number(1) AS T,
          to_nchar(property_value) as V, to_number(null) as VN, to_timestamp_tz(null) VT
      FROM T0
      UNPIVOT (
        property_value FOR K IN (
          "NAME" AS n'NAME'
        )
      )
    )
  UNION ALL
    SELECT VID, VL, K, T, to_nchar(VN, 'TM9', 'NLS_Numeric_Characters=''.,''') AS V, VN, VT
    FROM (
      SELECT
        VID, VL, K,
          to_number(4) AS T,
          to_nchar(null) as V, to_number(property_value) as VN, to_timestamp_tz(null) VT
      FROM T0
      UNPIVOT (
        property_value FOR K IN (
          "RATE" AS n'RATE'
        )
      )
    )
  )
)
INSERT /*+ append */ INTO "TEST_USER".TEST_20_4_GRAPHVT$
(VID, VL, K, T, V, VN, VT)
SELECT VID, VL, K, T, V, VN, VT
FROM (
  SELECT 
    VID, VL, K, T, V, VN, VT
  FROM(
    WITH T0 AS (
      SELECT /*+ materialize */
        round(sys_op_combined_hash(n'"N2"' || '|' || TABLE_KEY)/2) AS VID
        , VL, "ID", "NAME", "RATE"
      FROM ( 
        SELECT 
          "ID" AS TABLE_KEY
          , n'HOGE2' AS VL, "ID", "NAME", "RATE"
        FROM "TEST_USER"."N2"
      )
    )
    SELECT VID, VL, K, T, to_nchar(VN, 'TM9', 'NLS_Numeric_Characters=''.,''') AS V, VN, VT
    FROM (
      SELECT
        VID, VL, K,
          to_number(4) AS T,
          to_nchar(null) as V, to_number(property_value) as VN, to_timestamp_tz(null) VT
      FROM T0
      UNPIVOT (
        property_value FOR K IN (
          "ID" AS n'ID'
        )
      )
    )
  UNION ALL
    SELECT VID, VL, K, T, V, VN, VT
    FROM (
      SELECT
        VID, VL, K,
          to_number(1) AS T,
          to_nchar(property_value) as V, to_number(null) as VN, to_timestamp_tz(null) VT
      FROM T0
      UNPIVOT (
        property_value FOR K IN (
          "NAME" AS n'NAME'
        )
      )
    )
  UNION ALL
    SELECT VID, VL, K, T, to_nchar(VN, 'TM9', 'NLS_Numeric_Characters=''.,''') AS V, VN, VT
    FROM (
      SELECT
        VID, VL, K,
          to_number(4) AS T,
          to_nchar(null) as V, to_number(property_value) as VN, to_timestamp_tz(null) VT
      FROM T0
      UNPIVOT (
        property_value FOR K IN (
          "RATE" AS n'RATE'
        )
      )
    )
  )
)
INSERT /*+ append */ INTO "TEST_USER".TEST_20_4_GRAPHGE$
(EID, SVID, DVID, EL, K, T, V, VN, VT)
SELECT EID, SVID, DVID, EL, K, T, V, VN, VT
FROM (
  SELECT 
    EID, SVID, DVID, EL, K, T, V, VN, VT
  FROM(
    WITH T0 AS (
      SELECT /*+ materialize */
        round(sys_op_combined_hash(n'"E1"' || '|' || TABLE_KEY)/2) AS EID, SVID, DVID
        , EL
      FROM ( 
        SELECT 
          "ID" AS TABLE_KEY,
          round(sys_op_combined_hash(n'"N1"' || '|' || "N1_ID")/2) AS SVID,
          round(sys_op_combined_hash(n'"N2"' || '|' || "N2_ID")/2) AS DVID
          , n'EL' AS EL
        FROM "TEST_USER"."E1"
        WHERE "N1_ID" IS NOT NULL AND "N2_ID" IS NOT NULL
      )
    )
    SELECT 
      EID, SVID, DVID
      , EL,
      to_nchar(null) AS K,
      to_number(null) AS T,
      to_nchar(null) AS V,
      to_number(null) AS VN,
      to_timestamp_tz(null) AS VT
    FROM T0
  )
)
INSERT /*+ append */ INTO "TEST_USER".TEST_20_4_GRAPHVD$ 
SELECT VID, VL, CNT
FROM (SELECT VID, VL, COUNT(*) AS CNT
      FROM "TEST_USER".TEST_20_4_GRAPHVT$
      GROUP BY VID, VL)
INSERT /*+ append */ INTO "TEST_USER".TEST_20_4_GRAPHGT$ 
SELECT EID, EL, SVID, DVID, sys_op_numtoraw(sys_op_combined_hash(EL)) AS ELH, lengthb(EL) AS ELS, CNT
FROM (SELECT EID, EL, SVID, DVID, COUNT(*) AS CNT
      FROM "TEST_USER".TEST_20_4_GRAPHGE$
      GROUP BY EID, SVID, DVID, EL)
{ call opg_apis.create_pg(graph_owner => :1, graph_name => :2, dop => :3, tbs_set => null, options => 'SKIP_TABLE=T') }
{ call opg_apis.analyze_pg(graph_name => :1, degree => :2, cascade => DBMS_STATS.AUTO_CASCADE, no_invalidate => DBMS_STATS.AUTO_INVALIDATE, force => false, options => null, graph_owner => :3) }

nbconvert --to html で html コンバートしたノートブックで plotly のグラフが描画されない問題の解決

環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.6
BuildVersion:   18G4032

$ pip list | grep -E "jupyter|plotly"
jupyter                            1.0.0
jupyter-client                     5.2.4
jupyter-console                    6.0.0
jupyter-core                       4.4.0
jupyterlab                         2.1.2
jupyterlab-server                  1.1.3
plotly                             4.9.0

現象

  • 以下のコマンドで nbconvert --to html で html コンバートしたノートブックで plotly のグラフが描画されない
$ jyputer nbconvert --to html hoge.ipynb
  • 作成した html を開くと以下のようにグラフが表示されない

f:id:tmks0820:20200730161842p:plain

  • エラーを確認すると以下の通り
Uncaught Error: Script error for: plotly
http://requirejs.org/docs/errors.html#scripterror
    C https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js:8
    onScriptError https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js:29

f:id:tmks0820:20200730161958p:plain

解決策

  • plotly.graph_objects.Figure.show を実行するときに fig.show(renderer="jupyterlab")renderer オプションを付ければよい。

  • jupyter を使っている場合には、fig.show(renderer="jupyter") と指定すれば良い。

Connection Pool データソースの設定

前提

  • OS: Oracle Linux Server release 7.6
  • apache-tomcat: 9.0.34
  • java version: 1.8.0_241
    • Java(TM) SE Runtime Environment (build 1.8.0_241-b07)
    • Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)

手順

1. webapps/<コンテキスト名>/META-INF/context.xml に DataSource Resource の設定を登録する

<!--?xml version="1.0" encoding="UTF-8"?-->
<Context>
 <Resource
  name="jdbc/oracledb"
  auth="Container"
  type="oracle.jdbc.pool.OracleDataSource"
  factory="oracle.jdbc.pool.OracleDataSourceFactory"
  maxTotal="100"
  maxIdle="30"
  maxWaitMillis="10000"
  username="GRAPHUSER"
  password="WELCOME1"
  alternateUsernameAllowed="true"
  driverClassName="oracle.jdbc.driver.OracleDriver"
  url="jdbc:oracle:thin:graphuser/WELCOME1@oracle-db:1521/pdb1"
  defaultAutoCommit="false" />
</Context>

2. webapps/<コンテキスト名>/WEB-INF/web.xml に Connection Pool 用のリソースを参照できるように設定する

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1" metadata-complete="true">
    ....
    <resource-ref>
      <description>Connection Pool</description>
      <res-ref-name>jdbc/oracledb</res-ref-name>
      <res-type>oracle.jdbc.pool.OracleDataSource</res-type>
      <res-auth>Container</res-auth>
    </resource-ref>

3. java:comp/env/jdbc/oracledb のようにデータソースを lookup する

Effective C++ 第三章 その2

項目11: 未定義private関数よりも =delete を優先する

  • C++ が必要に応じて自動的に作成する関数 = 特殊メンバ関数
    • その代表がコピーコンストラクタとコピー代入演算子
    • C++98 では private にすることで対応していた
      • この関数にアクセスしようとしても、未リンクでエラーになる
template<class charT, class traits = char_traits<charT>>
class basic_ios : public ios_base {
  public:
       ...
  private:
     basic_ios(const basic_ios &); // not defined
     basic_ios& operator=(const basic_ios&); // not defined
}
  • C++11 では delete を使える
    • delete した場合、どうやっても呼び出せないし、これらの関数を呼び出そうとするとコンパイル時に発見することができる
template<class charT, class traits = char_traints<charT>>
class basic_ios : public ios_base
{
   public:
      ...
      basic_ios(const basic_ios& ) = delete;
      basic_ios& operator=(const basic_ios&) = delete;
}
  • さらに delete はどんな関数でも適用できる
class Hoge
{
   bool isLucky(int number);
   bool isLucky(char number)=delete;
   bool isLucky(bool number)=delete;
   bool isLucky(double number)=delete;
}
template<typename T>
void f(T* ptr)

template<>
void f<char>(char*) = delete;

template<>
void f<void>(void*) = delete;
  • クラス内テンプレート関数に対して、テンプレートの特殊化を用いて無効なインスタンス化を防ぐ場合、C++98 の場合の private にする方法は実現できない。テンプレートの特殊化はクラススコープではなく、名前空間スコープで定義する必要があるため。
    • delete の場合名前区間スコープで定義することができる。

項目12: オーバーライドする関数は override と宣言する

  • 仮想関数のオーバライドは以下のように行う
class Base
{
public:
    virtual void doWork() {
    };
};

class Derived: public Base
{
public:
    virtual void doWork() {
        //
    };
};

void _override()
{
    std::unique_ptr<Base> upb = 
        std::make_unique<Derived>();
    upb->doWork();
}
  • オーバーライドを正しく動作するように宣言するのは難しい。
    • オーバーライドする条件をすべて満たさない限り、別のメンバー関数として定義される。
    • 以下のケースでは、Derived2 クラスのメンバ関数はすべて Base2 クラスのメンバ関数のオーバーライドではない
class Base2
{
public:
    virtual void mf1() const {};
    virtual void mf2(int x) {};
    virtual void mf3() & {};
    void mf4() const {};
};

class Derived2
{
public:
    virtual void mf1() {};
    virtual void mf2(unsigned int x) {};
    virtual void mf3() && {};
    void mf4() const {};
};
  • override とすると、コンパイル時にオーバーライドできないことを発見できる
class Base2
{
public:
    virtual void mf1() const {};
    virtual void mf2(int x) {};
    virtual void mf3() & {};
    virtual void mf4() const {};
};

class Derived2 : public Base2
{
public:
    virtual void mf1() const override {};
    virtual void mf2(int x) override {};
    virtual void mf3() & override {};
    virtual void mf4() const override {};
};
  • メンバ関数の参照修飾子を用いると、左辺値オブジェクト、右辺値オブジェクト(*this)を区別できる

Effective C++ 第三章 その1

項目7: オブジェクト作成時の {} と ()の違い

  • 初期値の設定方法は以下の3つ
    • {}
    • =
    • ()
  • ユーザ定義型では、初期化構文によって呼び出される関数が異なる
Widget w1; // デフォルトコンストラクタを呼び出す
Widget w2 = w1; // コピーコンストラクタを呼び出す
w1 = w2; //コピー演算子=を呼び出す
  • C+11 から初期化の統一記法が導入された

    • {} による初期化
    • コンテナの初期要素の指定も可能に
    • 非スタティックなメンバ変数のデフォルト値の設定にも利用可能
    • コピー不可能なオブジェクト(std::atomic など) では、= による初期化はできない
    • 精度が落ちる変換を認めない
    • 最も厄介な構文解析の回避
  • {} による初期化の注意点

    • std::initializer_list 型の仮引数を持つコンスタラクタがある場合、実引数の型が std::initializer_list 内の型に変換する方法がある場合には、std::initializer_list 型の仮引数を持つコンストラクタが優先される
      • オブジェクトのコピーやムーブの場合も同様
  • std::vector も std::initalizer_list 型の仮引数を持つコンストラクタを実装しているため注意

  • テンプレート内のオブジェクト作成に () か {} を使うかは慎重に選択する必要がある

項目8: 0やNULL よりも nullptr を優先する

  • nullptr はすべてのポインタ型を表す型 (std::nullptr_t)
  • テンプレートが型推論するとき、0 や NULL を誤った型に推論するため、nullptr を選択する

項目9: typedef よりもエイリアス宣言を優先する

  • まず、関数型宣言がエイリアス宣言のほうがわかりやすい
typedef void (*FP1) (int, const std::string&);
using FP2 = void (*) Iint, const std::string&);
  • 最大の理由はテンプレート
    • エイリアス宣言はテンプレート化可能 (エイリアステンプレート)
    • typedef の場合、テンプレート内で従属型になるため、typename が必要となる
// typdef
template<typename T>
struct MyAllocList1 {
    typedef std::list<T, std::allocator<T>> type;
};

template<typename T>
class Widget2
{
private:
    typename MyAllocList1<T>::type list;
};

//型エイリアス
template<typename T>
using MyAllocList2 = std::list<T, std::allocator<T>>;

template<typename T>
class Widget3
{
private:
    MyAllocList2<T> list;
};
  • C++14 は、C+11での型変換特性のすべてをエイリアステンプレートとして実装している

項目10: enum にはスコープを設ける

  • 列挙子名が enum を定義したスコープに漏れ出すことから、C++98 の enum を unscoped enum という
// C++98 方式で enum を定義した場合、enumの列挙子名はenumを定義したスコープに含まれるため
// 以下のように実装するとコンパイルエラーとなる
//enum Color { black, white, red };
//auto white = false;
//erro! redefinition of 'white' as different kind of symbol
  • C+11 では unscoped enum の対語となる scoped enum が導入
enum class Color { black, white, red };
auto white = false;
Color c = Color::white;
  • unscoped enum名前空間の汚染以外に、汎整数型に暗黙的に変換されてしまうという問題点を抱えている。
void _f()
{
    enum UnScopedColor { _black, _white, _red };
    UnScopedColor c_ = _red;

    // double 型へ型変換される
    // scoped enum の場合コンパイルエラー
    // scoped enum でコンパイルを通す場合には、
    // std::static_cast<double> で明示的に型変換を行う
    if (c_ < 14.5)
    {
        // pass
    }
}
  • scoped enum は前方宣言も可能。つまり列挙子なしで enum を宣言可能。
// enum Hoge; はコンパイルエラー
enum class Hoge;
  • enum の基礎とする型をintから別のものへオーバーライドすることが可能
    • これを行うと、unscoped enum でも前方宣言が可能
enum class Status: std::uint32_t;
enum _Status: std::uint32_t;
  • unscoped enum が scoped enum よりも有用なケースは実はある
    • std::tuple 内フィールドを表す場合
using UserInfo = std::tuple<std::string,
                            std::string,
                            std::size_t>;
UserInfo uInfo;
// フィールド1 が何であるかを使う側が知っている必要がある...
auto val = std::get<1>(uInfo);

// unscoped enum を使うと...!
enum UserInfoFields { uiName, uiEmail, uiReputation };
auto val2 = std::get<uiName>(uInfo);

// scoped enum を使用すると冗長になる
enum class UserInfoFields2 { uiName, uiEmail, uiReputation };
auto val3 = std::get<static_cast<std::size_t>(UserInfoFields2::uiName)>(uInfo);
  • scoped enum で便利に、std::tuple の要素にアクセスするための関数テンプレート
// 任意の列挙子をとり、コンパイル時定数としてその値を返す関数テンプレート
template<typename E>
constexpr typename std::underlying_type<E>::type
  toUType(E enumerator) noexcept
{
    return static_cast<typename std::underlying_type<E>::type>(enumerator);
}

// C++14
template<typename E>
constexpr std::underlying_type_t<E> toUType2(E enumerator)
{
    return static_cast<std::underlying_type_t<E>>(enumerator);
}

// C++14 のさらに改良版
template<typename E>
constexpr auto toUType3(E enumerator)
{
    return static_cast<std::underlying_type_t<E>>(enumerator);
}

auto val4 = std::get<toUType(UserInfoFields::uiName)>(uInfo);
auto val5 = std::get<toUType2(UserInfoFields::uiName)>(uInfo);
auto val6 = std::get<toUType3(UserInfoFields::uiName)>(uInfo);

Effective C++ 第二章

項目5: 明示的宣言よりも auto を優先する

  • auto で宣言することのメリット
    • 複雑な型宣言の省略
template<typename It>
void f(It b, It e)
{
  while (b != e)
  {
     typename std::iterator_traits<It>::value_type cV = *b;
     // auto cV = *b;
   }
}
  • 未初期化のエラーを防げる
    • auto 宣言した場合には、初期化必須
  • コンパイラにしか分からない型(ラムダ式等)も表現可能
auto deffLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2; }
// C++14
auto deffLess = [](const auto& p1, const auto& p2) { return *p1 < *p2; }
  • C++11 で登場した std::function 型でもラムダ式は表現可能だが、以下の点で auto のほうが有利

  • 型のショートカットによる実行環境依存の問題を回避できる

std::vector<int> v;
unsigned sv = v.size();
// v.size() の戻り値の型は、std::vector<int>::size_type 型は、実行環境によりサイズが異なる
// Windows 32bit では、32bit
// Windows 64bit では、64bit
// 一方 unsigned は どちらも32bit
  • 型宣言のミスによる不要なオブジェクトのコピー及び未定義動作を回避可能
std::unordered_map<std::string, int> m;
// std::pair 型は std::pair<const std::string, int> が正解
// 型の不一致により、一時オブジェクトが生成され、ループ内の最後の処理で破棄される
for(const std::pair<std::string, int> p : m)
{
   ...
}
  • ただし、auto による型宣言にも落とし穴がある
    • 統一初期化子で初期化した変数の型が std::initializer_list になる
    • std::vector 型のコンテナに対して operator を実行すると、std::vector::reference が戻されることによる未定義動作の発生

項目6: auto が期待と異なる型を推論する場面では ETII を用いる

  • std::vector に対する operator はコンテナ要素の参照を返さない
    • C++ ではビットの参照を認めていない
    • std::vector の operator[] は T& を返す関数
    • bool& は返すことができないため、bool& のように振る舞うオブジェクトを返す
    • それが、std::vector::reference
      • これは Proxy Class の一例
        • Proxy Class には使用者にプロキシを意識させるものとそうでないものがある
          • 意識させるもの: スマートポインタ
          • 意識させないもの: std::vector::reference
            • これが auto との相性が悪い (ユーザに意識させないプロキシクラスは、ライフタイムを1文以内と想定している)
            • つまり、ユーザに意識させないプロキシクラスに対する auto 宣言は避けるべき
struct Widget {};

std::vector<bool> features(const Widget& w)
{
    std::vector<bool> v = { true, false, true, false, true };
    return v;
}

TEST(std_bool, features)
{
    Widget w;
    auto priorities = features(w)[4];

    EXPECT_STREQ("std::__1::__bit_reference<std::__1::vector<bool, std::__1::allocator<bool> >, true>",
                 boost::typeindex::type_id_with_cvr<decltype(priorities)>().pretty_name().c_str());

}
  • std::vector::referece が bool& の動作を模倣する技法はたくさんある
  • その一例が暗黙の型変換
bool priorities = features(w)[4];
  • auto 自体が問題ではなく、推論してほしい型を推論しないことが問題
    • よって目的の型への推論を強制すれば良い
    • それが、ETII (Explicitly typed initializer idiom)
auto priorities = std::static_cast<bool>(features(w)[4]);