Skip to content

Instantly share code, notes, and snippets.

@hikarin522
Created March 26, 2020 04:13
Show Gist options
  • Save hikarin522/384918acf2189d92089003cef3ded8d0 to your computer and use it in GitHub Desktop.
Save hikarin522/384918acf2189d92089003cef3ded8d0 to your computer and use it in GitHub Desktop.
Qiitaから移行
Error in user YAML: (<unknown>): mapping values are not allowed in this context at line 1 column 15
---
title: RxSwift: ObservableをSequenceで同期処理に変更する
tags: Swift RxSwift
author: hikarin522
slide: false
---
class SyncFIFO<T> {
    var buf = Array<T>()
    let lock = DispatchSemaphore(value: 1)
    let sem = DispatchSemaphore(value: 0)

    func push(_ val: T) {
        self.lock.wait()
        defer { self.lock.signal() }
        self.buf.append(val)
        self.sem.signal()
    }
    
    func popSync() -> T {
        sem.wait()
        lock.wait()
        defer { lock.signal() }
        return buf.removeFirst()
    }
}

extension ObservableType {
    func toSequenceSync<T>() -> AnySequence<Event<T>> where T == E {
        return AnySequence { () -> AnyIterator<Event<T>> in
            let buf = SyncFIFO<Event<T>?>()
            let disposable = self.subscribe(buf.push)
            return AnyIterator {
                guard let ev = buf.popSync() else {
                    disposable.dispose()
                    return nil
                }

                switch ev {
                case .error, .completed:
                    buf.push(nil)
                    fallthrough
                case .next:
                    return ev
                }
            }
        }
    }
}
  • AnySequence.makeIterator()subscribe
  • AnyIterator.next()Eventが流れてくるまでブロックします

※ デッドロックに注意

Error in user YAML: (<unknown>): mapping values are not allowed in this context at line 1 column 13
---
title: Swift: [Equatable]?の比較
tags: Swift
author: hikarin522
slide: false
---
func ==<Element: Equatable>(lhs: [Element]?, rhs: [Element]?) -> Bool {
    return lhs.map { l in rhs.map { l == $0 } ?? false }
        ?? rhs.map { r in lhs.map { $0 == r } ?? false }
        ?? true
}

こんなかんじで!=とかも実装すればいいと思う

Array<Element: Equatable>とかOptional<Wrapped: Equatable>Equatableだったらこんなわけわからんことせんで良かったのに

参考

https://qiita.com/Kuniwak/items/681d5d2c20e149e2a0ef

title tags author slide
LFS管理されてないUnityのバイナリ形式ファイルを検索する
Unity Git PowerShell
hikarin522
false

以下の記事の通り git ls-binary のエイリアス設定をする。 https://qiita.com/hikarin522/items/b3c884648afbf0674bb3

LFS管理でない *.asset のバイナリ版を検索する場合、git ls-binary *.asset のコマンドを実行する。

title tags author slide
Server Side Blazor + Google 認証 の最小構成メモ
Blazor .NETCore C# OpenID
hikarin522
false

Visual Studio の Server Side Blazor のプロジェクトテンプレートに Google 認証を追加する。

以下の ASP.NET Core Identity なしの Google 認証を行う。

https://docs.microsoft.com/aspnet/core/security/authentication/social/social-without-identity?view=aspnetcore-3.0

環境

  • Visual Studio 2019 16.2.0 Preview 4.0
  • .NET Core SDK 3.0.100-preview6-012264

手順

  • Visual Studio のプロジェクトテンプレートから「ASP.NET Core Web アプリケーション / Blazor サーバーアプリ」を選択し、プロジェクトを生成する。(認証なしで作成)

  • NuGet で以下のパッケージを追加する。
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="3.0.0-preview6.19307.2" />

  • Google Developer Console で OAuth のクライアントID等を取得し、appsettings.json か Secret Manager に設定する。 リダイレクトURLの設定とかもやる。( http://localhost:8080/signin-google とかを追加。※ポート番号確認すること) https://docs.microsoft.com/aspnet/core/security/app-secrets

"Authentication": {
  "Google": {
    "ClientId": "",
    "ClientSecret": ""
  }
}
  • Startup クラスを以下のように変更。
...
public void ConfigureServices(IServiceCollection services)
{
...
+   services.AddAuthentication(option => {
+       option.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
+       option.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
+   })
+   .AddCookie()
+   .AddGoogle(options => {
+       var google = this.Configuration.GetSection("Authentication:Google");
+           options.ClientId = google["ClientId"];
+           options.ClientSecret = google["ClientSecret"];
+   });

+   services.AddControllers();
   
    services.AddRazorPages();
    services.AddServerSideBlazor();
...
}
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
    app.UseRouting();

+   app.UseAuthentication();
+   app.UseAuthorization();

    app.UseEndpoints(endpoints => {
        endpoints.MapBlazorHub();
+       endpoints.MapDefaultControllerRoute();
        endpoints.MapFallbackToPage("/_Host");
    });
...
}
  • 以下の Shared/LoginDisplay.razor を作成する。
<AuthorizeView>
    <Authorized>
        <text>Hello, @context.User.Identity.Name!</text>
        <a href="Account/Logout">Log out</a>
    </Authorized>
    <NotAuthorized>
        <a href="Account/Login">Log in</a>
    </NotAuthorized>
</AuthorizeView>
  • MainLayout.razor を以下のように変更
...
<div class="main">
    <div class="top-row px-4">
+       <LoginDisplay />
        <a href="https://docs.microsoft.com/en-us/aspnet/" target="_blank">About</a>
    </div>
...
  • Controllers/AccountController.cs を作成し以下の AccountController クラスを作る。
public class AccountController: Controller
{
    public IActionResult Login()
    {
        return this.Challenge(new AuthenticationProperties {
            RedirectUri = "/"
        });
    }

    public IActionResult Logout()
    {
        return this.SignOut(new AuthenticationProperties {
            RedirectUri = "/"
    });
}
  • FetchData.razor 等のページに @attribute [Authorize] を付ける。

おわり。

※ これはとりあえず動くようにしただけなので Cookie の設定とかいろいろ後でちゃんとやること。リダイレクト周りとか。オープンリダイレクト注意。 https://docs.microsoft.com/aspnet/core/security/preventing-open-redirects

title tags author slide
FlatBuffersのチュートリアルやってみた (C++, Windows)
FlatBuffers
hikarin522
false

https://google.github.io/flatbuffers/index.html#flatbuffers_overview

  • バイナリシリアライズフォーマット
  • Google製 (2014年公開)
  • Apache License v2
  • C++, C#, C, Go, Java, JavaScript, PHP, Python, ...etc
  • スキーマ定義で型安全
  • オプションフィールドによりフォーマットに前方、後方互換性を持たせることが可能
  • スキーマレスVerもありJSONっぽく使用することも可?
  • unpackが超高速 (バッファ領域に直接オフセット指定アクセスするためデコード時のメモリアロケートコストなし)
  • ストリーミング処理とかmmapとかと相性がいいらしい
  • Cocos2d-x, Facebook, ...etc

インストール (v1.8.0, Windows)

以下からWindows用バイナリをダウンロードしてパスを通す https://github.com/google/flatbuffers/releases

チュートリアル (C++)

https://google.github.io/flatbuffers/flatbuffers_guide_tutorial.html

  • 準備
$ mkdir monster_sample
$ cd monster_sample
$ git init
$ git submodule add https://github.com/google/flatbuffers.git
$ cd flatbuffers
$ git checkout v1.8.0
$ cd ../
  • monster.fbsを作成

https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html

展開
// Example IDL file for our monster's schema.

namespace MyGame.Sample;

enum Color:byte { Red = 0, Green, Blue = 2 }

union Equipment { Weapon } // Optionally add more tables.

struct Vec3 {
  x:float;
  y:float;
  z:float;
}

table Monster {
  pos:Vec3; // Struct.
  mana:short = 150;
  hp:short = 100;
  name:string;
  friendly:bool = false (deprecated);
  inventory:[ubyte];  // Vector of scalars.
  color:Color = Blue; // Enum.
  weapons:[Weapon];   // Vector of tables.
  equipped:Equipment; // Union.
  path:[Vec3];        // Vector of structs.
}

table Weapon {
  name:string;
  damage:short;
}

root_type Monster;
  • monster.fbsをコンパイル
$ flatc --cpp monster.fbs

以下のmonster_generated.hが生成される

展開
// automatically generated by the FlatBuffers compiler, do not modify


#ifndef FLATBUFFERS_GENERATED_MONSTER_MYGAME_SAMPLE_H_
#define FLATBUFFERS_GENERATED_MONSTER_MYGAME_SAMPLE_H_

#include "flatbuffers/flatbuffers.h"

namespace MyGame {
namespace Sample {

struct Vec3;

struct Monster;

struct Weapon;

enum Color {
  Color_Red = 0,
  Color_Green = 1,
  Color_Blue = 2,
  Color_MIN = Color_Red,
  Color_MAX = Color_Blue
};

inline Color (&EnumValuesColor())[3] {
  static Color values[] = {
    Color_Red,
    Color_Green,
    Color_Blue
  };
  return values;
}

inline const char **EnumNamesColor() {
  static const char *names[] = {
    "Red",
    "Green",
    "Blue",
    nullptr
  };
  return names;
}

inline const char *EnumNameColor(Color e) {
  const size_t index = static_cast<int>(e);
  return EnumNamesColor()[index];
}

enum Equipment {
  Equipment_NONE = 0,
  Equipment_Weapon = 1,
  Equipment_MIN = Equipment_NONE,
  Equipment_MAX = Equipment_Weapon
};

inline Equipment (&EnumValuesEquipment())[2] {
  static Equipment values[] = {
    Equipment_NONE,
    Equipment_Weapon
  };
  return values;
}

inline const char **EnumNamesEquipment() {
  static const char *names[] = {
    "NONE",
    "Weapon",
    nullptr
  };
  return names;
}

inline const char *EnumNameEquipment(Equipment e) {
  const size_t index = static_cast<int>(e);
  return EnumNamesEquipment()[index];
}

template<typename T> struct EquipmentTraits {
  static const Equipment enum_value = Equipment_NONE;
};

template<> struct EquipmentTraits<Weapon> {
  static const Equipment enum_value = Equipment_Weapon;
};

bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *obj, Equipment type);
bool VerifyEquipmentVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);

MANUALLY_ALIGNED_STRUCT(4) Vec3 FLATBUFFERS_FINAL_CLASS {
 private:
  float x_;
  float y_;
  float z_;

 public:
  Vec3() {
    memset(this, 0, sizeof(Vec3));
  }
  Vec3(float _x, float _y, float _z)
      : x_(flatbuffers::EndianScalar(_x)),
        y_(flatbuffers::EndianScalar(_y)),
        z_(flatbuffers::EndianScalar(_z)) {
  }
  float x() const {
    return flatbuffers::EndianScalar(x_);
  }
  float y() const {
    return flatbuffers::EndianScalar(y_);
  }
  float z() const {
    return flatbuffers::EndianScalar(z_);
  }
};
STRUCT_END(Vec3, 12);

struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
  enum {
    VT_POS = 4,
    VT_MANA = 6,
    VT_HP = 8,
    VT_NAME = 10,
    VT_INVENTORY = 14,
    VT_COLOR = 16,
    VT_WEAPONS = 18,
    VT_EQUIPPED_TYPE = 20,
    VT_EQUIPPED = 22,
    VT_PATH = 24
  };
  const Vec3 *pos() const {
    return GetStruct<const Vec3 *>(VT_POS);
  }
  int16_t mana() const {
    return GetField<int16_t>(VT_MANA, 150);
  }
  int16_t hp() const {
    return GetField<int16_t>(VT_HP, 100);
  }
  const flatbuffers::String *name() const {
    return GetPointer<const flatbuffers::String *>(VT_NAME);
  }
  const flatbuffers::Vector<uint8_t> *inventory() const {
    return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_INVENTORY);
  }
  Color color() const {
    return static_cast<Color>(GetField<int8_t>(VT_COLOR, 2));
  }
  const flatbuffers::Vector<flatbuffers::Offset<Weapon>> *weapons() const {
    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Weapon>> *>(VT_WEAPONS);
  }
  Equipment equipped_type() const {
    return static_cast<Equipment>(GetField<uint8_t>(VT_EQUIPPED_TYPE, 0));
  }
  const void *equipped() const {
    return GetPointer<const void *>(VT_EQUIPPED);
  }
  template<typename T> const T *equipped_as() const;
  const Weapon *equipped_as_Weapon() const {
    return equipped_type() == Equipment_Weapon ? static_cast<const Weapon *>(equipped()) : nullptr;
  }
  const flatbuffers::Vector<const Vec3 *> *path() const {
    return GetPointer<const flatbuffers::Vector<const Vec3 *> *>(VT_PATH);
  }
  bool Verify(flatbuffers::Verifier &verifier) const {
    return VerifyTableStart(verifier) &&
           VerifyField<Vec3>(verifier, VT_POS) &&
           VerifyField<int16_t>(verifier, VT_MANA) &&
           VerifyField<int16_t>(verifier, VT_HP) &&
           VerifyOffset(verifier, VT_NAME) &&
           verifier.Verify(name()) &&
           VerifyOffset(verifier, VT_INVENTORY) &&
           verifier.Verify(inventory()) &&
           VerifyField<int8_t>(verifier, VT_COLOR) &&
           VerifyOffset(verifier, VT_WEAPONS) &&
           verifier.Verify(weapons()) &&
           verifier.VerifyVectorOfTables(weapons()) &&
           VerifyField<uint8_t>(verifier, VT_EQUIPPED_TYPE) &&
           VerifyOffset(verifier, VT_EQUIPPED) &&
           VerifyEquipment(verifier, equipped(), equipped_type()) &&
           VerifyOffset(verifier, VT_PATH) &&
           verifier.Verify(path()) &&
           verifier.EndTable();
  }
};

template<> inline const Weapon *Monster::equipped_as<Weapon>() const {
  return equipped_as_Weapon();
}

struct MonsterBuilder {
  flatbuffers::FlatBufferBuilder &fbb_;
  flatbuffers::uoffset_t start_;
  void add_pos(const Vec3 *pos) {
    fbb_.AddStruct(Monster::VT_POS, pos);
  }
  void add_mana(int16_t mana) {
    fbb_.AddElement<int16_t>(Monster::VT_MANA, mana, 150);
  }
  void add_hp(int16_t hp) {
    fbb_.AddElement<int16_t>(Monster::VT_HP, hp, 100);
  }
  void add_name(flatbuffers::Offset<flatbuffers::String> name) {
    fbb_.AddOffset(Monster::VT_NAME, name);
  }
  void add_inventory(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory) {
    fbb_.AddOffset(Monster::VT_INVENTORY, inventory);
  }
  void add_color(Color color) {
    fbb_.AddElement<int8_t>(Monster::VT_COLOR, static_cast<int8_t>(color), 2);
  }
  void add_weapons(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Weapon>>> weapons) {
    fbb_.AddOffset(Monster::VT_WEAPONS, weapons);
  }
  void add_equipped_type(Equipment equipped_type) {
    fbb_.AddElement<uint8_t>(Monster::VT_EQUIPPED_TYPE, static_cast<uint8_t>(equipped_type), 0);
  }
  void add_equipped(flatbuffers::Offset<void> equipped) {
    fbb_.AddOffset(Monster::VT_EQUIPPED, equipped);
  }
  void add_path(flatbuffers::Offset<flatbuffers::Vector<const Vec3 *>> path) {
    fbb_.AddOffset(Monster::VT_PATH, path);
  }
  explicit MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb)
        : fbb_(_fbb) {
    start_ = fbb_.StartTable();
  }
  MonsterBuilder &operator=(const MonsterBuilder &);
  flatbuffers::Offset<Monster> Finish() {
    const auto end = fbb_.EndTable(start_);
    auto o = flatbuffers::Offset<Monster>(end);
    return o;
  }
};

inline flatbuffers::Offset<Monster> CreateMonster(
    flatbuffers::FlatBufferBuilder &_fbb,
    const Vec3 *pos = 0,
    int16_t mana = 150,
    int16_t hp = 100,
    flatbuffers::Offset<flatbuffers::String> name = 0,
    flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory = 0,
    Color color = Color_Blue,
    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Weapon>>> weapons = 0,
    Equipment equipped_type = Equipment_NONE,
    flatbuffers::Offset<void> equipped = 0,
    flatbuffers::Offset<flatbuffers::Vector<const Vec3 *>> path = 0) {
  MonsterBuilder builder_(_fbb);
  builder_.add_path(path);
  builder_.add_equipped(equipped);
  builder_.add_weapons(weapons);
  builder_.add_inventory(inventory);
  builder_.add_name(name);
  builder_.add_pos(pos);
  builder_.add_hp(hp);
  builder_.add_mana(mana);
  builder_.add_equipped_type(equipped_type);
  builder_.add_color(color);
  return builder_.Finish();
}

inline flatbuffers::Offset<Monster> CreateMonsterDirect(
    flatbuffers::FlatBufferBuilder &_fbb,
    const Vec3 *pos = 0,
    int16_t mana = 150,
    int16_t hp = 100,
    const char *name = nullptr,
    const std::vector<uint8_t> *inventory = nullptr,
    Color color = Color_Blue,
    const std::vector<flatbuffers::Offset<Weapon>> *weapons = nullptr,
    Equipment equipped_type = Equipment_NONE,
    flatbuffers::Offset<void> equipped = 0,
    const std::vector<const Vec3 *> *path = nullptr) {
  return MyGame::Sample::CreateMonster(
      _fbb,
      pos,
      mana,
      hp,
      name ? _fbb.CreateString(name) : 0,
      inventory ? _fbb.CreateVector<uint8_t>(*inventory) : 0,
      color,
      weapons ? _fbb.CreateVector<flatbuffers::Offset<Weapon>>(*weapons) : 0,
      equipped_type,
      equipped,
      path ? _fbb.CreateVector<const Vec3 *>(*path) : 0);
}

struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
  enum {
    VT_NAME = 4,
    VT_DAMAGE = 6
  };
  const flatbuffers::String *name() const {
    return GetPointer<const flatbuffers::String *>(VT_NAME);
  }
  int16_t damage() const {
    return GetField<int16_t>(VT_DAMAGE, 0);
  }
  bool Verify(flatbuffers::Verifier &verifier) const {
    return VerifyTableStart(verifier) &&
           VerifyOffset(verifier, VT_NAME) &&
           verifier.Verify(name()) &&
           VerifyField<int16_t>(verifier, VT_DAMAGE) &&
           verifier.EndTable();
  }
};

struct WeaponBuilder {
  flatbuffers::FlatBufferBuilder &fbb_;
  flatbuffers::uoffset_t start_;
  void add_name(flatbuffers::Offset<flatbuffers::String> name) {
    fbb_.AddOffset(Weapon::VT_NAME, name);
  }
  void add_damage(int16_t damage) {
    fbb_.AddElement<int16_t>(Weapon::VT_DAMAGE, damage, 0);
  }
  explicit WeaponBuilder(flatbuffers::FlatBufferBuilder &_fbb)
        : fbb_(_fbb) {
    start_ = fbb_.StartTable();
  }
  WeaponBuilder &operator=(const WeaponBuilder &);
  flatbuffers::Offset<Weapon> Finish() {
    const auto end = fbb_.EndTable(start_);
    auto o = flatbuffers::Offset<Weapon>(end);
    return o;
  }
};

inline flatbuffers::Offset<Weapon> CreateWeapon(
    flatbuffers::FlatBufferBuilder &_fbb,
    flatbuffers::Offset<flatbuffers::String> name = 0,
    int16_t damage = 0) {
  WeaponBuilder builder_(_fbb);
  builder_.add_name(name);
  builder_.add_damage(damage);
  return builder_.Finish();
}

inline flatbuffers::Offset<Weapon> CreateWeaponDirect(
    flatbuffers::FlatBufferBuilder &_fbb,
    const char *name = nullptr,
    int16_t damage = 0) {
  return MyGame::Sample::CreateWeapon(
      _fbb,
      name ? _fbb.CreateString(name) : 0,
      damage);
}

inline bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *obj, Equipment type) {
  switch (type) {
    case Equipment_NONE: {
      return true;
    }
    case Equipment_Weapon: {
      auto ptr = reinterpret_cast<const Weapon *>(obj);
      return verifier.VerifyTable(ptr);
    }
    default: return false;
  }
}

inline bool VerifyEquipmentVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
  if (values->size() != types->size()) return false;
  for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
    if (!VerifyEquipment(
        verifier,  values->Get(i), types->GetEnum<Equipment>(i))) {
      return false;
    }
  }
  return true;
}

inline const MyGame::Sample::Monster *GetMonster(const void *buf) {
  return flatbuffers::GetRoot<MyGame::Sample::Monster>(buf);
}

inline bool VerifyMonsterBuffer(
    flatbuffers::Verifier &verifier) {
  return verifier.VerifyBuffer<MyGame::Sample::Monster>(nullptr);
}

inline void FinishMonsterBuffer(
    flatbuffers::FlatBufferBuilder &fbb,
    flatbuffers::Offset<MyGame::Sample::Monster> root) {
  fbb.Finish(root);
}

}  // namespace Sample
}  // namespace MyGame

#endif  // FLATBUFFERS_GENERATED_MONSTER_MYGAME_SAMPLE_H_
  • エンコードするプログラムの作成
展開
#include <iostream>
#include <fstream>
using namespace std;

#include "monster_generated.h"
using namespace MyGame::Sample;

int main() {

	// 作業領域確保 (初期値1KB)
	flatbuffers::FlatBufferBuilder builder(1024);

	// name, hp, mana, pos
	auto name = builder.CreateString("Orc");
	int hp = 300;
	int mana = 150;
	auto pos = Vec3(1.0f, 2.0f, 3.0f);

	// Weapon 生成
	auto weapon_one_name = builder.CreateString("Sword");
	short weapon_one_damage = 3;
	auto sword = CreateWeapon(builder, weapon_one_name, weapon_one_damage);

	auto weapon_two_name = builder.CreateString("Axe");
	short weapon_two_damage = 5;
	auto axe = CreateWeapon(builder, weapon_two_name, weapon_two_damage);

	std::vector<flatbuffers::Offset<Weapon>> weapons_vector;
	weapons_vector.push_back(sword);
	weapons_vector.push_back(axe);
	auto weapons = builder.CreateVector(weapons_vector);

	// inventory
	unsigned char treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
	auto inventory = builder.CreateVector(treasure, 10);

	// path
	Vec3 points[] = { Vec3(1.0f, 2.0f, 3.0f), Vec3(4.0f, 5.0f, 6.0f) };
	auto path = builder.CreateVectorOfStructs(points, 2);

	// Monster 生成
#if 1 // どちらでも可
	auto orc = CreateMonster(builder, &pos, mana, hp, name,
							inventory, Color_Red, weapons,
							Equipment_Weapon, axe.Union(), path);
#else
	MonsterBuilder monster_builder(builder);
	monster_builder.add_pos(&pos);
	monster_builder.add_mana(mana);
	monster_builder.add_hp(hp);
	monster_builder.add_name(name);
	monster_builder.add_inventory(inventory);
	monster_builder.add_color(Color_Red);
	monster_builder.add_weapons(weapons);
	monster_builder.add_equipped_type(Equipment_Weapon);
	monster_builder.add_equipped(axe.Union());
	monster_builder.add_path(path);
	auto orc = monster_builder.Finish();
#endif

#if 1 // どちらでも可
	builder.Finish(orc);
#else
	FinishMonsterBuffer(builder, orc);
#endif

	// バッファ取得 ※This must be called after `Finish()`.
	uint8_t *buf = builder.GetBufferPointer();
	size_t size = builder.GetSize();

	// ファイルに書き込み
 	ofstream fout;
    fout.open("monster.bin", ios::out|ios::binary|ios::trunc);
    if (!fout) {
        return 1;
    }
    fout.write((char*)buf, size);
    fout.close();
}
  • デコードするプログラムの作成
展開
#include <iostream>
#include <fstream>
using namespace std;

#include "monster_generated.h"
using namespace MyGame::Sample;

int main() {

	// ファイル読み込み
 	ifstream fin("monster.bin", ios::in | ios::binary);
    if (!fin) {
        return 1;
    }
	auto begin = fin.tellg();
	fin.seekg(0, fin.end);
	auto end = fin.tellg();
	fin.clear();
	fin.seekg(0, fin.beg);
	auto len = end - begin;
	auto buf = new char[len + 1];
	fin.read(buf, len);
    fin.close();

	// バッファ設定
	auto monster = GetMonster((uint8_t*)buf);

	// hp, mana, name
	printf("hp: %d\n", monster->hp());
	printf("mana: %d\n", monster->mana());
	printf("name: %s\n", monster->name()->c_str());

	// inventory
	printf("inventory: [");
	for (auto val: *monster->inventory()) {
		printf("%d, ", val);
	}
	printf("\b\b]\n");

	// weapons
	printf("weapons: [");
	for (auto wp: *monster->weapons()) {
		printf("{\n");
		printf("\tname: %s\n", wp->name()->c_str());
		printf("\tdamage: %d\n", wp->damage());
		printf("}, ");
	}
	printf("\b\b]\n");

	// equipped
	auto union_type = monster->equipped_type();
	if (union_type == Equipment_Weapon) {
		// Requires `static_cast` to type `const Weapon*`.
		auto weapon = static_cast<const Weapon*>(monster->equipped());
		printf("equipped: Weapon{\n");
		printf("\tname: %s\n", weapon->name()->c_str());
		printf("\tdamage: %d\n", weapon->damage());
		printf("}\n");
	}

	delete[] buf;
}
  • コンパイル (g++.exe (x86_64-win32-seh-rev1, Built by MinGW-W64 project) 7.2.0)
$ g++ -I flatbuffers/include -o encoder.exe encoder.cpp
$ g++ -I flatbuffers/include -o decoder.exe decoder.cpp
  • エンコード
$ ./encode.exe

monster.bin.png

  • デコード
$ ./decode.exe
hp: 300
mana: 150
name: Orc
inventory: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
weapons: [{
        name: Sword
        damage: 3
}, {
        name: Axe
        damage: 5
}]
equipped: Weapon{
        name: Axe
        damage: 5
}
title tags author slide
rustの開発環境構築メモ @ Windows & Sublime Text 3
Rust
hikarin522
false

rust のインストール

$ cargo install cargo-edit
  • rustfmt: フォーマットツール
$ cargo install rustfmt
  • racer: 補完ツール
    動かすのにrust本体のソースコードが必要なのでダウンロードする
    https://www.rust-lang.org/en-US/downloads.html
    解凍したフォルダの中のsrcフォルダのパスを環境変数RUST_SRC_PATHにセットする
    あとHOMEの.cargoのパスもCARGO_HOMEにセットする
$ cargo install racer
$ setx RUST_SRC_PATH path/to/rust/src
$ setx CARGO_HOME %USERPROFILE%/.cargo

Sublime Text のパッケージ

Package Controlでパッケージを入れる

  • BeautifyRust: rustfmtを動かすやつ
{
  "run_on_save": false,
  "rustfmt": "C:\\Users\\name\\.cargo\\bin\\rustfmt.exe"
}

run_on_save: true にするとセーブ時に自動でrustfmtするようになるけどうざいからoffにした あとrustfmtへのパスは絶対パスで指定しないと動かないときがある

  • RustAutoComplete: racerを動かすやつ
{
  "racer": "C:\\Users\\name\\.cargo\\bin\\racer.exe",
  "search_paths": ["path/to/rust/src"]
}

これも絶対パス指定したほうがいい search_pathsには環境変数RUST_SRC_PATHに指定したのと同じものを設定する

  • SublimeLinter
  • SublimeLinter-contrib-rustc: 構文チェック
{
...
  "linters": {
    "rust": {
      "@disable": false,
      "args": [],
      "crate-root": null,
      "excludes": [],
      "use-cargo": true,
      "use-cargo-check": false,
      "use-crate-root": false
    }
  }
...
}

lintersのとこにrustの設定を追加する


参考にした記事 Rustをはじめよう! Rustの環境構築 Atom & SublimeText

title tags author slide
BOOST_PP_REPEATの仕組み
C boost preprocess macro
hikarin522
false

これは 高知工科大 Advent Calendar 2017 の13日目の記事です。

OBなのに書かされるマンです。 なぜだか知りませんが私はマイコンの処女を奪う仕事をしていることになっているようです。まぁこういう仕事をしているとC++が使えないのは当たり前としてC99ですら使えないだとか、なぜこの時代にXPのCygwinなんだとか、VB6って何者なん?とか、このコード俺と同い年やんとかのありとあらゆる拷問を受けることがあります。:innocent:

なので今回はCコンパイラが:poop:という拷問を受けた時にも味方してくれる心強いライブラリ Boost Preprocessor について書くことにします。


環境

Boost PP とは

みんな大好きC++の標準ライブラリboostの中のプリプロセッサ用ライブラリです。プリプロセッサなので当然Cからも使えますし、最悪の場合、現代のコンパイラ付属のプリプロセッサで処理した後:poop:コンパイラに通すなんてこともできちゃいます。 http://www.boost.org/doc/libs/1_65_1/libs/preprocessor/doc/index.html

今回はBoost PPのなかでもよくお世話になるものをいくつか紹介します。

※ boost 1.60.0 からはVariadic Macro Data Library(VMD)なるものもあるようです。

BOOST_PP_IF(cond, t, f)

まずはウォーミングアップ。BOOST_PP_IF です。 これはマクロの展開を制御するものですがとりあえず以下のように使えます。

/* 展開前 */
printf(BOOST_PP_IF(10, "TRUE", "FALSE"));
printf(BOOST_PP_IF(0, "TRUE", "FALSE"));

/* 展開後 */
printf("TRUE");
printf("FALSE");

ちょっとよくわかりませんね。手動で順番に展開してみます。 BOOST_PP_IFcontrol/if.hppで以下のように定義されてます。

#define BOOST_PP_IF(cond, t, f) BOOST_PP_IIF(BOOST_PP_BOOL(cond), t, f)

ここで BOOST_PP_IIFBOOST_PP_BOOL がでてきました。

BOOST_PP_BOOL(x)

これは引数を判定して 1又は0に展開されるマクロで、logical/bool.hppで以下のように定義されてます。

#define BOOST_PP_BOOL(x)    BOOST_PP_BOOL_I(x)
#define BOOST_PP_BOOL_I(x)  BOOST_PP_BOOL_ ## x
#define BOOST_PP_BOOL_0     0
#define BOOST_PP_BOOL_1     1
#define BOOST_PP_BOOL_2     1
...
#define BOOST_PP_BOOL_256 1

恐ろしい定義ですね。しかしこれで0から256までの数字を以下のように0/1に展開できるようになります。

BOOST_PP_BOOL(10)
-> BOOST_PP_BOOL_I(10)
-> BOOST_PP_BOOL_ ## 10
-> BOOST_PP_BOOL_10
-> 1

BOOST_PP_IIF(bit, t, f)

これはbitが0ならfに、1ならtに展開されるマクロでBOOST_PP_IFの本体です。

control/iif.hppで定義されてます。

#define BOOST_PP_IIF(bit, t, f)    BOOST_PP_IIF_I(bit, t, f)
#define BOOST_PP_IIF_I(bit, t, f)  BOOST_PP_IIF_ ## bit(t, f)
#define BOOST_PP_IIF_0(t, f)       f
#define BOOST_PP_IIF_1(t, f)       t

展開は以下のようになります。

BOOST_PP_IIF(1, "TRUE", "FALSE")
-> BOOST_PP_IIF_I(1, "TRUE", "FALSE")
-> BOOST_PP_IIF_ ## 1("TRUE", "FALSE")
-> BOOST_PP_IIF_1("TRUE", "FALSE")
-> "TRUE"

これで BOOST_PP_IF が以下のように展開されることがわかりましたね。

BOOST_PP_IF(10, "TRUE", "FALSE")
-> BOOST_PP_IIF(BOOST_PP_BOOL(10), "TRUE", "FALSE")
-> BOOST_PP_IIF(1, "TRUE", "FALSE")
-> "TRUE"

BOOST_PPではこのようにトークンの結合##で展開の制御をしたり、大量の単調なdefineがあったりするのはごく当たり前のことになっていますのでこの機会にぜひ慣れてください。 あとトークンの結合にはBOOST_PP_CATを使ったりもします。

cat.hpp

#define BOOST_PP_CAT(a, b)    BOOST_PP_CAT_I(a, b)
#define BOOST_PP_CAT_I(a, b)  a ## b

なぜ BOOST_PP_CAT が必要なのかは以下の記事を参考にしてください。 字句の結合や文字列化を行う際のマクロ置換動作をよく理解する

BOOST_PP_REPEAT(count, macro, data)

さて本題 BOOST_PP_REPEAT です。 これはプリプロセッサで繰り返し処理をするためのマクロで以下のように展開されます。

/* 展開前 */
BOOST_PP_REPEAT(count, macro, data)

/* 展開後 */
macro(z, 0, data) macro(z, 1, data) ... macro(z, count - 1, data) 

なのでこれを使うと以下のようなことができます。(zは内部(ループの最適化)で使う値なので今は気にしなくで大丈夫です)

#define SUM(z, n, data)  BOOST_PP_EXPR_IF(n, +) BOOST_PP_INC(n)
// BOOST_PP_EXPR_IF(n, +)はn != 0 なら + に展開される
// BOOST_PP_INC(n)は n + 1 に展開される

BOOST_PP_REPEAT(10, SUM, _);
-> SUM(z, 0, _) SUM(z, 1, _) ... SUM(z, 9, _);
-> 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10;

これほんとにマクロなのか? しかしBOOST_PP_REPEATの恐ろしさはこんなものではありません。なんと2重ループができるのです。

#define MUL(z, n, _)  BOOST_PP_EXPR_IF(n, *) (BOOST_PP_REPEAT(BOOST_PP_INC(n), SUM, _))
#define SUM(z, n, _)  BOOST_PP_EXPR_IF(n, +) BOOST_PP_INC(n)

BOOST_PP_REPEAT(10, MUL, _);
-> MUL(z, 0, _) MUL(z, 1, _) ... MUL(z, 9, _);
-> (BOOST_PP_REPEAT(1, SUM, _)) * (BOOST_PP_REPEAT(2, SUM, _)) * ... * (BOOST_PP_REPEAT(10, SUM, _));
-> (1) * (1 + 2) * (1 + 2 + 3) * ... * (1 + 2 + ... + 10);

あれ?おかしいですね。BOOST_PP_REPEATの展開中にまたBOOST_PP_REPEATが出てきています。プリプロセッサでは再帰できないはずなのに。

実際以下のようなマクロは正しく展開されません。

#define X(seq)            X_I seq
#define X_I(elem, isEnd)  X_ ## isEnd(elem)
#define X_0(elem)         elem + X_I
#define X_1(elem)         elem

X((a, 0)(b, 0)(c, 1))
-> a + X_I(b, 0)(c, 1)

これは

X((a, 0)(b, 0)(c, 1))
-> X_I(a, 0)(b, 0)(c, 1)
-> X_##0(a)(b, 0)(c, 1)
-> X_0(a)(b, 0)(c, 1)
-> a + X_I(b, 0)(c, 1)
-> a + b + X_I(c, 1)
-> a + b + c

と展開してほしいところですがX_Iの展開中にX_Iが再度出てきているため a + X_I(b, 0)(c, 1) までしか展開されません。※展開できるVerを記事の最後に書きました。

gccのドキュメントにもちゃんと書いてます。3.10.5 Self-Referential Macros

なぜBOOST_PP_REPEATは再帰できているのか。

repetition/repeat.hpp

#define BOOST_PP_REPEAT  BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))

なるほど。関数マクロではないようです。 BOOST_PP_CATはトークンを結合するものなので BOOST_PP_REPEAT_BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4) を結合しているのは分かりました。 でも BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4) っていったい何者なんだ

BOOST_PP_AUTO_REC(pred, n)

これはあるマクロが展開済みかどうかを2分探索で判別し、その展開回数を返すマクロです。 ほんと何言ってるか訳が分からないと思いますが順番に見ていきましょう。 ※ BOOST_PP_REPEATでは n == 4 なので4のやつだけ抜粋

detail/auto_rec.hpp

#define BOOST_PP_AUTO_REC(pred, n)  BOOST_PP_NODE_ENTRY_ ## n(pred)

#define BOOST_PP_NODE_ENTRY_4(p)    BOOST_PP_NODE_2(p)(p)

#define BOOST_PP_NODE_2(p)          BOOST_PP_IIF(p(2), BOOST_PP_NODE_1, BOOST_PP_NODE_3)
#define BOOST_PP_NODE_1(p)          BOOST_PP_IIF(p(1), 1, 2)
#define BOOST_PP_NODE_3(p)          BOOST_PP_IIF(p(3), 3, 4)

これは以下のように展開されます

BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4)
-> BOOST_PP_NODE_ENTRY_4(BOOST_PP_REPEAT_P)
-> BOOST_PP_NODE_2(BOOST_PP_REPEAT_P)(BOOST_PP_REPEAT_P)
-> BOOST_PP_IIF(BOOST_PP_REPEAT_P(2), BOOST_PP_NODE_1, BOOST_PP_NODE_3)(BOOST_PP_REPEAT_P)

ここで BOOST_PP_IIF が出てきたので、これは BOOST_PP_NODE_1 又は BOOST_PP_NODE_3 に展開されます。

  • BOOST_PP_NODE_1 のとき (BOOST_PP_REPEAT_P(2) == 1)
-> BOOST_PP_IIF(BOOST_PP_REPEAT_P(2), BOOST_PP_NODE_1, BOOST_PP_NODE_3)(BOOST_PP_REPEAT_P)
-> BOOST_PP_NODE_1(BOOST_PP_REPEAT_P)
-> BOOST_PP_IIF(BOOST_PP_REPEAT_P(1), 1, 2)

また BOOST_PP_IIF が出てきたので 1 又は 2 に展開されます。

  • BOOST_PP_NODE_3 のとき (BOOST_PP_REPEAT_P(2) == 0)

これも同様に展開すると最終的に 3 又は 4 に展開されます。

表にすると以下のようになります。

展開後
BOOST_PP_REPEAT_P(2) == 1 BOOST_PP_REPEAT_P(1) == 1 1
BOOST_PP_REPEAT_P(2) == 1 BOOST_PP_REPEAT_P(1) == 0 2
BOOST_PP_REPEAT_P(2) == 0 BOOST_PP_REPEAT_P(3) == 1 3
BOOST_PP_REPEAT_P(2) == 0 BOOST_PP_REPEAT_P(3) == 0 4

こういうやつどっかで見たことありませんか? そう、これは BOOST_PP_REPEAT_P(n) が1を返す最小のnを2分探索で求めていたやつなんですね。 ※ BOOST_PP_REPEAT_P(n) == 1 なら BOOST_PP_REPEAT_P(n + 1) == 1 も成り立つ

これで BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4) が 1 ~ 4 のどれかに展開されることが判明したので、BOOST_PP_REPEAT は以下のように展開されます。

BOOST_PP_REPEAT
-> BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))
-> BOOST_PP_REPEAT_ ## 1 // n == 1
-> BOOST_PP_REPEAT_1

なので先ほどの2重ループの例は次のようになります。

#define MUL(z, n, _)  BOOST_PP_EXPR_IF(n, *) (BOOST_PP_REPEAT(BOOST_PP_INC(n), SUM, _))
#define SUM(z, n, _)  BOOST_PP_EXPR_IF(n, +) BOOST_PP_INC(n)

BOOST_PP_REPEAT(10, MUL, _);
-> BOOST_PP_REPEAT_1(10, MUL, _);
-> MUL(z, 0, _) MUL(z, 1, _) ... MUL(z, 9, _);
-> (BOOST_PP_REPEAT(1, SUM, _)) * (BOOST_PP_REPEAT(2, SUM, _)) * ... * (BOOST_PP_REPEAT(10, SUM, _));

なるほど。ここで2回目に出てきたBOOST_PP_REPEATBOOST_PP_REPEAT_2になってくれればBOOST_PP_REPEAT_1の再帰にならずに済みそうです。 ※ BOOST_PP_REPEATの展開はBOOST_PP_REPEAT_1に展開された時点でいったん終わっているので再帰にはなっていません。

やっとここまで来ましたがまだ BOOST_PP_REPEAT_P(n) が残っています。もうしんどい 😇

BOOST_PP_REPEAT_P(n)

repetition/repeat.hpp

#define BOOST_PP_REPEAT_P(n)  BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_REPEAT_ ## n(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL))

#define BOOST_PP_REPEAT_CHECK_BOOST_PP_NIL                1
#define BOOST_PP_REPEAT_CHECK_BOOST_PP_REPEAT_1(c, m, d)  0
#define BOOST_PP_REPEAT_CHECK_BOOST_PP_REPEAT_2(c, m, d)  0
#define BOOST_PP_REPEAT_CHECK_BOOST_PP_REPEAT_3(c, m, d)  0

/* tuple/eat.hpp */
#define BOOST_PP_TUPLE_EAT_3(e0, e1, e2)

とりあえず n == 2 を展開してみましょう。

BOOST_PP_REPEAT_P(2)
-> BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_REPEAT_ ## 2(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL))
-> BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_REPEAT_2(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL))
-> BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3(z, 0, BOOST_PP_NIL))
-> BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_NIL)
-> BOOST_PP_REPEAT_CHECK_ ## BOOST_PP_NIL
-> BOOST_PP_REPEAT_CHECK_BOOST_PP_NIL
-> 1

1に展開されました。次 n == 1 のとき。 これも同様に展開すると 1になります。 なるほど。BOOST_PP_REPEAT_P(2) == 1 && BOOST_PP_REPEAT_P(1) == 1 だから BOOST_PP_REPEAT_1 になるのか。

しかしちょっと待ってください。 BOOST_PP_REPEAT_P(1)の展開中にはBOOST_PP_REPEAT_1が出てきますよね。 これ2回目のループ(BOOST_PP_REPEAT_1の展開中)の場合展開できないはずです。 では展開できない場合をやってみましょう。

BOOST_PP_REPEAT_P(1)
-> BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_REPEAT_ ## 1(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL))
-> BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_REPEAT_1(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL))
-> BOOST_PP_REPEAT_CHECK_ ## BOOST_PP_REPEAT_1(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL)
-> BOOST_PP_REPEAT_CHECK_BOOST_PP_REPEAT_1(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL)
-> 0

なんとぉ! 0に展開されました。 n == 2, 3 の時も同様に BOOST_PP_REPEAT_n が展開できないとき BOOST_PP_REPEAT_P(n) は 0に展開されます。 なんて恐ろしいマクロなんだ。

でもまぁこれで2回目のループの時はBOOST_PP_REPEAT_P(2) == 1 && BOOST_PP_REPEAT_P(1) == 0 だから BOOST_PP_REPEAT_2 になりました。

2重ループの例は次のようになります。

#define MUL(z, n, _)    BOOST_PP_EXPR_IF(n, *) (BOOST_PP_REPEAT(BOOST_PP_INC(n), SUM, _))
#define SUM(z, n, _)    BOOST_PP_EXPR_IF(n, +) BOOST_PP_INC(n)

BOOST_PP_REPEAT(10, MUL, _);
-> BOOST_PP_REPEAT_1(10, MUL, _);
-> MUL(z, 0, _) MUL(z, 1, _) ... MUL(z, 9, _);
-> (BOOST_PP_REPEAT(1, SUM, _)) * (BOOST_PP_REPEAT(2, SUM, _)) * ... * (BOOST_PP_REPEAT(10, SUM, _));
-> (BOOST_PP_REPEAT_2(1, SUM, _)) * (BOOST_PP_REPEAT_2(2, SUM, _)) * ... * (BOOST_PP_REPEAT_2(10, SUM, _));
-> (1) * (1 + 2) * (1 + 2 + 3) * ... * (1 + 2 + ... + 10);

たしかに、これだと再帰してませんね。 ※ ちなみに BOOST_PP_REPEAT は3重ループまで出来ます。がここまで読んだあなたなら4重目以降も実装できますよね 😇

BOOST_PP_REPEAT_n(count, macro, data)

最後の敵はこいつ BOOST_PP_REPEAT_nBOOST_PP_REPEAT の本体ですがこいつはそんなに強くないので大丈夫です。

repetition/repeat.hpp

#define BOOST_PP_REPEAT_1(c, m, d)    BOOST_PP_REPEAT_1_I(c, m, d)
#define BOOST_PP_REPEAT_1_I(c, m, d)  BOOST_PP_REPEAT_1_ ## c(m, d)

#define BOOST_PP_REPEAT_1_0(m, d)
#define BOOST_PP_REPEAT_1_1(m, d)     m(2, 0, d)
#define BOOST_PP_REPEAT_1_2(m, d)     BOOST_PP_REPEAT_1_1(m, d) m(2, 1, d)
#define BOOST_PP_REPEAT_1_3(m, d)     BOOST_PP_REPEAT_1_2(m, d) m(2, 2, d)
...
#define BOOST_PP_REPEAT_1_255(m, d)   BOOST_PP_REPEAT_1_254(m, d) m(2, 254, d)
#define BOOST_PP_REPEAT_1_256(m, d)   BOOST_PP_REPEAT_1_255(m, d) m(2, 255, d)

BOOST_PP_REPEAT_1は上記のようになってるので展開すると以下のようになります。

BOOST_PP_REPEAT_1(10, macro, data)
-> BOOST_PP_REPEAT_1_I(10, macro, data)
-> BOOST_PP_REPEAT_1_ ## 10(macro, data)
-> BOOST_PP_REPEAT_1_10(macro, data)
-> BOOST_PP_REPEAT_1_9(macro, data) macro(2, 9, data)
-> BOOST_PP_REPEAT_1_8(macro, data) macro(2, 8, data) macro(2, 9, data)
...
-> macro(2, 0, data) macro(2, 1, data) ... macro(2, 9, data) 

これまた圧倒的に単調な定義ですが、これで256まではループっぽいことが出来ました。

あと最初にzはループ最適化用だとか言ってましたがこれは次のループのnのことでした。 2重ループの例の MUL

#define MUL(z, n, _)  BOOST_PP_EXPR_IF(n, *) (BOOST_PP_REPEAT_ ## z(BOOST_PP_INC(n), SUM, _))

のように定義すると BOOST_PP_AUTO_REC を使わずに BOOST_PP_REPEAT_2 を生成できるようになるのでプリプロセス時間が少し短縮されるようになります。

おわりに

どうでしたか? これであなたもBOOST_PP大好き人間になりましたか? BOOST_PP_FORとかのループ系のやつはほぼ同じような感じになっているのでもう読めるようになってると思います。:innocent: あと特殊なのは BOOST_PP_ITERATE とか BOOST_PP_SLOT とかが残ってますがもうしんどいので勘弁してください。 この記事が読めたあなたならきっと自力で解読できるはずです。

あとBOOST_PPはPSoCととても相性がいいです。PSoCを使っている方は今すぐBOOST_PPを使うように。


展開できない例を展開できるようにしてみた

#define X(seq)             X_I0 seq
#define X_I0(elem, isEnd)  X_ ## isEnd(elem, 1)
#define X_I1(elem, isEnd)  X_ ## isEnd(elem, 0)
#define X_0(elem, next)    elem + X_I ## next
#define X_1(elem, next)    elem

X((a, 0)(b, 0)(c, 1))      // X開始
-> X_I0(a, 0)(b, 0)(c, 1)  // X_I0開始
-> X_0(a, 1)(b, 0)(c, 1)
-> a + X_I1(b, 0)(c, 1)    // X_I0終了, X_I1開始
-> a + X_0(b, 0)(c, 1)
-> a + b + X_I0(c, 1)      // X_I1終了, X_I0開始
-> a + b + X_1(c, 1)
-> a + b + c               // X_I0終了, X終了

X_I0, X_0は2回出てきてるけどX_I0の展開中にX_I0は出てきていない(途中で切れ目がある)のでOK

Error in user YAML: (<unknown>): mapping values are not allowed in this context at line 1 column 15
---
title: RxSwift: nilフィルター
tags: Swift RxSwift
author: hikarin522
slide: false
---
extension ObservableType {
    func filterNil<T>() -> Observable<T> where E == Optional<T> {
        return flatMap(Observable.from(optional:))
    }

    func filterNil<T, R>(_ transform: @escaping (E) throws -> Optional<R>) -> Observable<R> where E == Optional<T> {
        return flatMap { Observable.from(optional: try $0.flatMap(transform)) }
    }

    func filterNil<R>(_ transform: @escaping (E) throws -> Optional<R>) -> Observable<R> {
        return map(transform).filterNil()
    }
}

参考

https://qiita.com/ryotapoi/items/925a74ed4df9cb351c32

title tags author slide
Rustのhyperがopenssl絡みでコンパイル出来ないときの対処法 (Windows編)
Rust
hikarin522
false

環境

最初のエラー

まずcargo newします。

$ cargo new --bin hyper_hello

そして Cargo.toml

...
[dependencies]
hyper = "0.9"

hyper を追加して cargo run すると

$ cargo run
...
...
Build failed, waiting for other jobs to finish...
failed to run custom build command for `openssl v0.7.11`
Process didn't exit successfully: `%USERPROFILE%\hyper_hello\target\debug\build\openssl-5eb24e119ff837c6\build-script-build` (exit code: 101)
--- stderr
thread '<main>' panicked at 'explicit panic', %USERPROFILE%\.cargo\registry\src\github.com-88ac128001ac3a9a\gcc-0.3.28\src\lib.rs:840
note: Run with `RUST_BACKTRACE=1` for a backtrace.

と怒られますが、これはhyperが使っているrust-opensslのエラーです。 なのでこれのREADMEの通りにMSYS2を入れるかOpenSSLを入れるかして下さい。 私はOpenSSLを入れる方でやったので今回はこの方法で話を進めます。

OpenSSLをインストールしたあとREADME通りに環境変数を設定してcargo runします。ちなみにインストールする場所はCドライブ直下じゃなくてもいいです。あとVisual Studio 2013も必要なのでインストールしておいてください。2015でもいいです。

$ setx OPENSSL_LIB_DIR "C:/OpenSSL-Win64"
$ setx OPENSSL_INCLUDE_DIR "C:/OpenSSL-Win64/include"
$ cargo run
...
Hello, world!

2つ目のエラー

とりあえずデフォルトのHello, worldはコンパイル出来たのでmain.rsをいじります。 とりあえずhyperのサンプルをコピペします。

extern crate hyper;

use hyper::Server;
use hyper::server::Request;
use hyper::server::Response;

fn hello(_: Request, res: Response) {
    res.send(b"Hello World!").unwrap();
}

fn main() {
    Server::http("127.0.0.1:3000").unwrap()
        .handle(hello).unwrap();
}

そしてcargo run

$ cargo run

...
error: linking with `link.exe` failed: exit code: 1181
note: ...
...
note: Non-UTF-8 output: LINK : fatal error LNK1181: ... \'ssl32.lib\' ...
error: aborting due to previous error
Could not compile `hyper_hello`.

どうやらリンクに失敗しているようです。よく見るとssl32.libeay32.libが見つからないそうなのでコピーします。

$ copy C:\OpenSSL-Win64\lib\VC\libeay32MT.lib C:\OpenSSL-Win64\eay32.lib
$ copy C:\OpenSSL-Win64\lib\VC\ssleay32MT.lib C:\OpenSSL-Win64\ssl32.lib

そしてcargo run

$ cargo run
   Compiling hyper_hello v0.1.0 (hyper_hello)
     Running `target\debug\hyper_hello.exe`

コンパイルできましたか? あとはブラウザでlocalhost:3000にアクセスすると**Hello World!**が表示されるはずです。

title tags author slide
git-submodule と git-subtree
Git
hikarin522
false

高知工科大 Advent Calendar 2016 4日目の\ヒッカリ~ン/です ネタ無いな~と思ってたんですが、最近gitのライブラリ管理で困ってる人を見かけたのでしょうがないからsubmoduleとsubtreeのまとめ記事を書こうと思います。

とか偉そうなこと言ってますが全部合ってる自信ないので間違ってたらマサカリコメント入れてください。(できれば枕投げコメント程度にやさしくお願いします)

git-submodule

git submoduleは外部のgitリポジトリを自分のgitリポジトリのサブディレクトリ取り込み、その特定のcommitを参照するものです。 とりあえずやってみましょう。

submoduleの追加

例えば、現在fooというリポジトリのルートにいるとして、lib/barフォルダにbarというリポジトリを取り込みたいとします。

$ git submodule add https://github.com/s-nlf-fh/bar.git lib/bar

これでlib/barフォルダにbarリポジトリのmasterが取り込まれました。 .gitmodulesというsubmodule管理用のファイルも作成されてその中身は

[submodule "lib\\bar"]
	path = lib\\bar
	url = https://github.com/s-nlf-fh/bar.git

となっており、ディレクトリのパスとURLを保存しています。 次にgit diffを実行してみます。

$ git diff --cached lib/bar
diff --git a/lib/bar b/lib/bar
new file mode 160000
index 0000000..b44a41b
--- /dev/null
+++ b/lib/bar
@@ -0,0 +1 @@
+Subproject commit b44a41b85eba12390534152c5a9e97793ed0b768

ここでbarリポジトリのcommit hashが出てきました。つまりsubmoduleはmasterとかdevelopとかのブランチではなく特定のコミットを参照するようになっていることがわかります。

submoduleを含むプロジェクトのclone

barを含んだfooプロジェクトをcloneするには以下のコマンドを打ちます。

$ git clone https://github.com/s-nlf-fh/foo.git
$ git submodule init
$ git submodule update

cloneしただけではlib/barフォルダは空っぽなのでsubmoduleの初期化とチェックアウトが必要です。 3行もあってめんどくさいですが実は以下の1行で同じことができます。

$ git clone --recursive https://github.com/s-nlf-fh/foo.git

これでlib/barフォルダは特定のコミットにチェックアウトされた状態のbarリポジトリになります。

参照するコミットの変更

barモジュールのmasterが更新されました。現在barモジュールは以前のコミットを参照するようになっていますが、最新のmasterを使いたいとします。その時はlib/barフォルダに移動してmasterをpullすればいいだけです。

$ cd lib/bar
$ git pull
$ cd ../../
$ git diff lib/bar
$ git add lib/bar
$ git commit -m "[barモジュール] 最新のmasterを使うようにする"

これでdiffで出力されるcommit hashが最新のmasterのものになります。 最新のものではなく特定のcommitを使いたいときはgit pullgit checkout `commit hash` に変えればいいです。 また今回のように最新のmasterに追従するだけとかdevelopブランチに追従するだけとかだとgit submodule update --remoteの一行でもできるのですがそれは公式ドキュメントに丸投げします。git-submodule

submoduleの編集

さてここからが本番です。 submoduleでbarを追加しましたがbarを編集したくなりました。なのでlib/barのフォルダ内を編集するのですがその前にすることがあります。 まず1つ目。barを編集するということはbarリポジトリにpushする権限がないといけません。なので他人のGitHubリポジトリ等をsubmoduleにする場合はまずforkしてそれをsubmoduleにしましょう。途中から変更も出来ます。 2つ目。必ずbarに新しいブランチを作ってから作業しましょう。

$ cd lib/bar
$ git checkout -b feature/foo

すでに作ってあるときは普通にチェックアウト

$ cd lib/bar
$ git checkout feature/foo

とにかく編集する前になんらかのブランチにチェックアウトすることが重要です!!! 理由はググればいっぱい出てきます。コミットが消えるとかなんとか。 実はというかsubmoduleは特定のコミットを追跡するものなので、親リポジトリ側でgit submodule updateするとサブリポジトリ内でgit checkout `commit hash` したのと同じ状態になります。つまり**git submodule updateしたらsubmodule内で行った変更は無くなるし、HEADも'detached HEAD'になります**。 これがsubmoduleのデメリットとして書かれてたりしますが、そもそもgit checkout `commit hash` したら変更が消えるのは当然だし、'detached HEAD'の状態でcommitして、ましてやその後またgit checkout `commit hash` して名無しの枝分かれを作るなんてこと普通はしませんよね。 git submodule updategit checkout `commit hash` と同だとわかってたらそんなことにはなりませんし、正しい使い方をすればいいだけの話です。 "学習コストがどうの"とか"submoduleやめてsubtree使うべき"とか書かれてたりもしますが、subtreeだって正しい使い方を勉強しないとまともに使えなしデメリットもあるんだから適材適所で使っていったらいいと思います。(マジレス)

話が逸れましたが、チェックアウトすればもう安心。barを編集してcommitしても大丈夫です。もうお分かりかと思いますがlib/barフォルダ内はbarリポジトリそのものになっています。lib/barに移動すればfooリポジトリのサブディレクトリとしてではなくbarリポジトリとしてgitの操作ができますので作ったブランチ(feature/foo)をdevelopやmasterにmergeしたりしてbarの開発をしてください。

最後にもう1つ注意点。親リポジトリ側(foo)でsubmoduleの参照を更新するコミットをする場合は必ずsubmodule側(bar)でpushして変更をサーバーに上げること。submoduleは特定のコミットを参照するものなのでローカルにしか存在しないコミットを参照されても困ります。

あと親リポジトリ側でmergeするときにコンフリクトしちゃうとめんどくさいとかありますけど、普通のコンフリクト解消に毛が生えた程度の対応でなんとかなると思います。(私が複雑なコンフリクトを経験してないだけかもですが) 対処法はまた公式ドキュメントに投げます。git-submodule

もうちょっと注意点あったと思いますが、とりあえずこのへんを覚えていれば何とかなると思います。

submoduleまとめ

  • submoduleは特定のcommit hashだけを追跡する
  • 親プロジェクトとライブラリプロジェクトの開発を完全に分離できる
  • ライブラリプロジェクトの更新を追跡するだけ(編集はしない)の用途には向いてる (npm的な使い方)
  • ライブラリの編集はするけどライブラリとしての変更(後でmasterやdevelopにmergeする)ならあり
  • ライブラリを特定のアプリ用にポーティングしたい場合とかには向かないと思う (理由はライブラリ側のブランチがポーティングした数だけ増え続けて爆発するから)

git-subtree

git-subtreeは外部のgitリポジトリを自分のgitリポジトリのブランチとして取り込み、そのブランチを丸ごとサブディレクトリに配置するものです。

ブランチをサブディレクトリに配置とは一体ってなると思いますがgit-subtreeの前にまずsubtree-mergeというものを紹介したいと思います。

subtree-merge

subtree-mergeとはマージ戦略のことです。マージ戦略とはgit mergeするときのマージのしかた、つまりアルゴリズムのことです。gitのmergeコマンドはマージの仕方をオプションでいろいろ選択出来るようになっていてそのうちの一つにsubtreeというものがあります。 merge-strategies

このsubtree-mergeを使うとサブディレクトリにライブラリを取り込んで管理することが出来ます。 とりあえずやってみましょう。 submoduleの時と同じでfooプロジェクトのlib/barディレクトリにbarライブラリを入れることにします。

ライブラリのリモートの追加

まずはfooにbarのリモート参照を追加してbarのmasterブランチにチェックアウトします。

$ git remote add bar_remote https://github.com/s-nlf-fh/bar.git
$ git fetch bar_remote
$ git checkout -b bar/master bar_remote/master

これでfooプロジェクトにbar/masterブランチが追加されてその中身はbarのmasterと同じになっています。

サブディレクトリへの取り込み

次はfooのmasterにbar/masterブランチを取り込みましょう。

$ git checkout master
$ git read-tree --prefix=lib/bar -u bar/master
$ git commit -m "lib/barにbarのmasterを取り込む"

これでlib/barフォルダの中身がbarのmasterと同じになりました。

ライブラリの変更に追従する

barプロジェクトが更新されたらそれに追従しないといけません。なのでpullします。

$ git checkout bar/master
$ git pull

これだけ。普通にbar/masterブランチに行ってpullすればいいだけです。 あとはfooのmasterに取り込み

$ git checkout master
$ git merge --allow-unrelated-histories -s subtree bar/master

これでmasterのlib/barディレクトリがbarの最新のmasterと同じになりました。 注意点としてはgitの2.9 ? からマージするときに--allow-unrelated-historiesのオプションを付けないと共通の親を持ってないコミットをマージ出来ないようになってるので付けましょう。

ライブラリの編集

さてlib/barフォルダ内を編集しようと思います。submoduleと違って編集前にすることはありません。普通に編集してコミットしてしまいましょう。

次にmasterブランチで行ったlib/barフォルダ内の更新をbar/masterブランチに反映させましょう。 ※これもpushの権限が必要です。がsubmoduleと違いmasterブランチにlib/barの変更コミットは存在するので無理にbarリポジトリにpushしなくても大丈夫です (ポーティングする時とか)

$ git checkout bar/master
$ git merge -s subtree master
$ git push

これでfooリポジトリのmasterで行った変更をbarリポジトリに取り込むことが出来ました。

まぁここに書いたことは全部公式ドキュメントにも書いてあります。 公式の方がわかりやすいのでこっちを読みましょうw

git-subtree

さて本題のgit-subtreeの話に入りたいと思いますが、まずgit-subtreeのソースコードをちらっと見てください。シェルスクリプトで書かれてます。 git-subtree.sh 見ましたか? "read-tree"とか"merge -s subtree"とかが書いてあるのを発見できましたか? そうです。subtree-mergeを使ったライブラリ管理の手順を使いやすいよういい感じにまとめてくれてるのがgit-subtreeのコマンドなんです。(ここちょっとマサカリ飛んできそうw)

中身がわかったら安心ですね。"マージするときコンフリクトしたんやけど"とか"subtree push / subtree pull出来ひんくなった"とかのトラブルにも対処できると思います。頑張ってください。 ちなみにgit-subtree.shの中のgit merge -s subtreeしてる所で--allow-unrelated-historiesのオプションが付いてないせいでrefusing to merge unrelated historiesってエラーになったことがあります。まぁ使い方が悪かっただけかもしれませんが。

もうしんどくなってきたので使い方はググったりgit subtree --helpしたりしてください。

subtreeまとめ

  • git-subtreeの中身ではsubtree-mergeを使ってる
  • 親プロジェクトのローカルリポジトリ内には2つのプロジェクトが入ってる状態になってちょっと複雑になる
  • 親プロジェクトの編集のコミットとライブラリプロジェクトの編集のコミットをちゃんと分けないとぐちゃぐちゃになる (分けろよって話ですが)
  • ポーティングするときとかはsubmoduleよりこっち使った方がいいかも
  • ライブラリプロジェクトへpushする権限がなくても編集したりできる (submoduleみたいにforkしなくても大丈夫)

さいごにまじれす

もしかしてあなたこれ全部読んだんですか? 暇人ですね。そんな時間があるならさっさと公式ドキュメント読みながら自分でやってみようぜ。そっちの方が数百倍わかりやすいと思うよ。

title tags author slide
UbuntuにPowerShell入れるメモ
PowerShell Ubuntu
hikarin522
false

環境

  • Ubuntu 18.04

手順

PowerShell 入れる

https://docs.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-6#ubuntu-1804

# Download the Microsoft repository GPG keys
wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb

# Register the Microsoft repository GPG keys
sudo dpkg -i packages-microsoft-prod.deb

# Update the list of products
sudo apt update

# Enable the "universe" repositories
sudo add-apt-repository universe

# Install PowerShell
sudo apt install powershell

# Start PowerShell
pwsh

ログインシェルをPowerShellにする

chsh -s /usr/bin/pwsh

posh-git 入れる

https://git-scm.com/book/ja/v2/Appendix-A%3A-%E3%81%9D%E3%81%AE%E4%BB%96%E3%81%AE%E7%92%B0%E5%A2%83%E3%81%A7%E3%81%AEGit-Powershell%E3%81%A7Git%E3%82%92%E4%BD%BF%E3%81%86

Install-Module posh-git

$PROFILE 編集

vim $PROFILE
# プロンプト設定 (お好みで変えて下さい)
function prompt {
    $tmp = $LASTEXITCODE
    $GitPromptSettings.EnableWindowTitle = "$(git rev-parse --git-common-dir | Split-Path -Parent | Split-Path -Leaf) "
    $path = git rev-parse --show-prefix
    if ($null -eq $path) {
        $path = $ExecutionContext.SessionState.Path.CurrentLocation.Path
    } else {
        $path = ':' + $path -replace '/$', ''
    }
    Write-Host $path -NoNewline
    Write-VcsStatus
    "$('>' * ($NestedPromptLevel + 1)) "
    $LASTEXITCODE = $tmp
}

# posh-git
Import-Module posh-git
$GitPromptSettings.EnableStashStatus = $true

# ~/bin をPATHに追加
$env:PATH += ":/home/$env:USER/bin"

# エイリアス (適当にいろいろ入れてね)
function exec() {
    Start-Process -Wait @args
}

New-Alias eval Invoke-Expression
New-Alias g git

# ssh-agent
Start-SshAgent
title tags author slide
Boost Multi-index Containers をいい感じにする
C++ boost
hikarin522
false

Boost Multi-index Containers Library を使うと複数のキーを持つコンテナが作れる

例えば以下の様なメンバを持ったクラスがあるとする

struct Item {
    int item_id;
    int item_category_id;
    std::string name;
    std::string description;
    // etc...
};

すると multi_index_container を使って以下の様に書ける

// タグの定義
struct tag_item_id { };
struct tag_item_category_id { };
struct tag_item_name { };

// コンテナ型の定義
using item_container_t = boost::multi_index_container<
    std::shared_ptr<Item>, // 格納する値の型
    boost::multi_index::indexed_by<
        boost::multi_index::hashed_unique< // 1つ目のキーは item_id
            boost::multi_index::tag<tag_item_id>,
            boost::multi_index::member<Item, int, &Item::item_id>
        >,
        boost::multi_index::hashed_non_unique< // 2つ目のキーは item_category_id
            boost::multi_index::tag<tag_item_category_id>,
            boost::multi_index::member<Item, int, &Item::item_category_id>
        >,
        boost::multi_index::hashed_non_unique< // 3つ目のキーは name
            boost::multi_index::tag<tag_item_name>,
            boost::multi_index::member<Item, std::string, &Item::name>
        >
    >
>;

// コンテナ
item_container_t items;
// 色々入れる
// items.insert(std::make_shared<Item>(...));
// ... 

// item_id が 100 のやつを取得
auto it_id_100 = items.get<tag_item_id>().find(100);

// item_category_id が 10 のやつを取得
auto it_category_10 = items.get<tag_item_category_id>().equal_range(10);

// name が "hoge" のやつを取得
auto it_hoge = items.get<tag_item_name>().find("hoge");

これでいいのだがコンテナ型の定義が長すぎて非常にめんどくさい 特にメンバ変数の型とかいちいち書くのだるいしメンバポインタ渡してるんだからそれから取得してほしい が、下記ドキュメントではそれはできないからあきらめろと書いてある 💢

https://www.boost.org/doc/libs/release/libs/multi_index/doc/tutorial/key_extraction.html#member

It might seem that the first and second parameters are superfluous, since the type of the base object and of the associated data field are already implicit in the pointer to member argument: unfortunately, it is not possible to extract this information with current C++ mechanisms, which makes the syntax of member a little too verbose.

しかし、これは嘘である 今のC++にできないことなどないはずである

なので頑張っていい感じにするヘルパーを作ってみた

https://gist.github.com/hikarin522/31bcdb5cd3437554f6f3f6380c35ac75

これを導入すると以下の様に簡単に書ける

// タグの定義
struct tag_item_name { };

// コンテナ型の定義
using item_container_t = boost::multi_index_container<
    std::shared_ptr<Item>, // 格納する値の型
    boost::multi_index::indexed_by<
        IDX(&Item::item_id)::hashed_unique<>,                // 1つ目のキーは item_id
        IDX(&Item::item_category_id)::hashed_non_unique<>,   // 2つ目のキーは item_category_id
        IDX(&Item::name, tag_item_name)::hashed_non_unique<> // 3つ目のキーは name, tag型も指定できる
    >
>;

// コンテナ
item_container_t items;
// 色々入れる
// items.insert(std::make_shared<Item>(...));
// ... 

// item_id が 100 のやつを取得
auto it_id_100 = items.get<IDX(&Item::item_id)>().find(100);

// item_category_id が 10 のやつを取得
auto it_category_10 = items.get<IDX(&Item::item_category_id)>().equal_range(10);

// name が "hoge" のやつを取得
auto it_hoge = items.get<IDX(&Item::name)>().find("hoge");
auto it_hoge = items.get<tag_item_name>().find("hoge"); // タグ型でもいける

IDX マクロで ##__VA_ARGS__ を使ってるので gcc じゃないとだめ ちなみに C++17 だと IDX マクロは不要になる https://cpprefjp.github.io/lang/cpp17/declaring_non-type_template_arguments_with_auto.html

参考文献

https://www.boost.org/doc/libs/release/libs/multi_index/ https://boostjp.github.io/tips/multi_index.html https://faithandbrave.hateblo.jp/entry/20091126/1259226643

title tags author slide
ReadyNAS の LAG 使用時の IP を DHCP で設定するときのメモ
rtx1210 ReadyNas DHCP
hikarin522
false
  • ReadyNAS214: 6.10.1
  • RTX1210: Rev.14.01.34

ReadyNASのDHCPクライアントはRFC2131完全対応してないらしく、クライアント識別に chaddr フィールドを使用しているようである。

なのでRTX1210のDHCPに dhcp server rfc2131 compliant except use-clientid を設定すれば行ける。

http://www.rtpro.yamaha.co.jp/RT/manual/rt-common/dhcp/dhcp_server_rfc2131_compliant.html

title tags author slide
Windows10 の OpenSSH 対応で posh-git の ssh-agent で鍵が取れなくなった時の対応
Windows10 Git posh-git SSH ssh-agent
hikarin522
false

原因

posh-git の Start-SshAgent コマンドでは Windows版OpenSSHのssh-agentを起動しているのにGit側は別のsshを使っているためにssh-agentを起動しているのに毎回鍵を聞かれるようになってしまう。

対策

GitにWindows版OpenSSHのsshを使わせる。 $git config -e --global で .gitconfig に以下の設定を追加する。

[core]
  sshCommand = C:/Windows/System32/OpenSSH/ssh.exe
title tags author slide
gitでLFSじゃないバイナリを検索する
Git lfs
hikarin522
false

.gitconfigに以下のエイリアス設定を追加する

[alias]
ls-binary = !"func() { git ls-files --eol \"$@\" | grep -v \"attr/-text\" | grep \"[iw]/-text\"; }; func"

git ls-binary で列挙できる。

補足

  • grep -v "attr/-text".gitattributes で明示的に -text 属性を指定したファイルを除外 (git lfs track で追加される)
  • grep "[iw]/-text" でgitがtextとして認識してないファイルを抽出
    ※ utf-8でないファイルも検出されます。
title tags author slide
UnityのIL2CPPで式木使う方法メモ
Unity IL2CPP C#
hikarin522
false

環境

  • Unity2019.1.7f1

方法

以下の link.xmlAssets 直下に置く。

<linker>
  <assembly fullname="System.Core">
    <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" />
  </assembly>
</linker>

簡単な式木なら Compile() 出来るようになります。(インタプリタで実行されます)

MakeGenericMethod 等使う場合は以下の記事の UsedOnlyForAOTCodeGeneration の例のように事前に生成しておいて下さい。 https://docs.unity3d.com/ja/2019.1/Manual/ScriptingRestrictions.html

Expression.Lambda する Delegate の引数や戻り値に値型があると厳しい。

Unity Test Runner の Play mode でいろいろ試せるので頑張って下さい。 https://docs.unity3d.com/ja/2019.1/Manual/testing-editortestsrunner.html

title tags author slide
Makefile のディレクトリを取得
Makefile
hikarin522
false

Makefileの先頭に

MAKEFILE_DIR := $(dir $(lastword $(MAKEFILE_LIST)))

と書くと MAKEFILE_DIR でそのMakefileが存在するディレクトリ名が取得できる。

※ Makefile の変数 CURDIR はカレントディレクトリなので make -f で呼び出した時や他のMakefileからインクルードされた時はそのMakefileが存在するディレクトリとは異なることがあります。

参考

https://qiita.com/Shigets/items/27170827707e5136ee89 https://qiita.com/chibi929/items/b8c5f36434d5d3fbfa4a

title tags author slide
enable_shared_from_this の使い方メモ
C++ C++11
hikarin522
false

https://cpprefjp.github.io/reference/memory/enable_shared_from_this.html https://ja.cppreference.com/w/cpp/memory/enable_shared_from_this

enable_shared_from_thisshared_ptr で管理したいクラスのベースにするやつだが、これをそのまま使うのは非常に危険が危ない。

危ない理由一覧

  • 絶対shared_ptr管理するためにファクトリ関数以外から new とかされたくない
  • コピーとかもされたくない
  • 多重継承したときにやばい
  • etc...
  • C++17で多少マシにはなったけどまだまだ危険が危ない

参考資料

なので 絶対shared_ptrで管理させるマン なるいい感じのやつ作ってみた。

shared_base.hpp

#pragma once
#include <memory>
#include <type_traits>

namespace util {
// SFINAE用
template <bool val>
using en_if = typename std::enable_if<val, std::nullptr_t>::type;

// メンバポインタのshared_ptrを取得する
template <class T, class U, class M, en_if<std::is_base_of<U, T>::value> = nullptr>
auto mem_ptr(const std::shared_ptr<T> &p, M U::*pm)
    -> std::shared_ptr<typename std::remove_reference<decltype(p.get()->*pm)>::type>
{
    return p == nullptr || pm == nullptr ? nullptr
        : decltype(mem_ptr(p, pm))(p, &(p.get()->*pm));
}

// 内部クラス
// これを直接継承しない
class virtual_shared_base: public std::enable_shared_from_this<virtual_shared_base>
{
protected:
    // 外部でインスタンス化されないようコンストラクタは全部protected
    // 派生先でもprotectedにしておくこと
    // ※ make_sharedで使うからprivateにはしない
    virtual_shared_base() = default;

public:
    // 全部shared_ptr管理したいのでコピーもムーブも禁止
    // 派生先でも定義しないこと
    virtual_shared_base(const virtual_shared_base&) = delete;    
    virtual_shared_base(virtual_shared_base&&) = delete;
    virtual_shared_base& operator = (const virtual_shared_base&) = delete;
    virtual_shared_base& operator = (virtual_shared_base&&) = delete;
    virtual ~virtual_shared_base() = default;

private:
    // SFINAE用
    template <class T>
    using base_check = en_if<std::is_base_of<virtual_shared_base, T>::value>;

protected:
    // std::make_sharedの代わり
    // 派生先のファクトリ関数から使う用
    // ※ publicコンストラクタがないとstd::make_sharedが使えないため
    template <class T, class ...Arg, base_check<T> = nullptr>
    static std::shared_ptr<T> make_shared(Arg &&...arg) {
        struct Dummy: T {
            Dummy(Arg &&...arg): T(std::forward<Arg>(arg)...) { }
        };
        return std::make_shared<Dummy>(std::forward<Arg>(arg)...);
    }

    // shared_from_this()の代わり
    // ダウンキャストして自身の型のshared_ptrを取る
    // get_shared(this) の様に使う
    template <class T, base_check<T> = nullptr>
    static std::shared_ptr<T> get_shared(T *self) {
        return self == nullptr ? nullptr
            : std::shared_ptr<T>(self->virtual_shared_base::shared_from_this(), self);
    }

    // メンバポインタの取得
    // mem_ptr(this, &TypeName::memberName) の様に使う
    template <class T, class U, class M, base_check<T> = nullptr>
    static auto mem_ptr(T *self, M U::*pm)
        -> decltype(util::mem_ptr(get_shared(self), pm))
    {
        return util::mem_ptr(get_shared(self), pm);
    }
};

// enable_sahred_from_thisをいい感じにラップしたやつ
// enable_sahred_from_thisの代わりにこれを継承する
template <class T>
class shared_base: virtual public virtual_shared_base
{
protected:
    shared_base() = default;

public:
    std::shared_ptr<T> shared_from_this() {
        return get_shared(static_cast<T*>(this));
    }
    std::shared_ptr<const T> shared_from_this() const {
        return get_shared(static_cast<const T*>(this));
    }
};
} // namespace util

使い方

  • enable_sahred_from_this と同じように shared_basepublic 継承する
  • コンストラクタを必ず protected にする
  • 静的メンバ関数とかフレンド関数とかのファクトリメソッドから make_shared 使って生成する
  • thisポインタは get_shared(this), メンバのポインタは mem_ptr(this, &T::mem) で取るようにして絶対に生のポインタを外に出さないこと
#include <iostream>
#include <string>
#include <functional>
#include "shared_base"

class Base1: public util::shared_base<Base1>
{
public:
    int val;

    // ファクトリ関数
    static std::shared_ptr<Base1> Create(int val) {
        return make_shared<Base1>(val);
    }

protected:
    // コンストラクタはprotectedで
    Base1(int v): val(v) { }
};

class Base2: public util::shared_base<Base2>
{
// コンストラクタは必ずprotectedにすること
protected:
    Base2() = default;
};

// 多重継承してもOK
class C: public Base1, public Base2 {
public:
    std::string str;

    static std::shared_ptr<C> Create(int v, std::string s) {
        return make_shared<C>(v, s);
    }

protected:
    C(int v, std::string s): Base1(v), str(s) { }

public:
    // メンバポインタ取得
    std::shared_ptr<int> get_val_ref() {
        // &(this->val) としたくなるが絶対やらないこと
        return mem_ptr(this, &C::val);
    }

    std::function<std::shared_ptr<int>()> get_func() {
        // 絶対に生のthisポインタをbindしないこと
        return std::bind(&C::get_val_ref, get_shared(this));
    }

    std::function<std::string()> get_str_func() const {
        // 絶対にthisキャプチャしないこと
        return [self = get_shared(this)]() { return self->str; };
    }
};

int main()
{
    using namespace std;

    shared_ptr<Base1> base1 = Base1::Create(1);
    shared_ptr<C> c = C::Create(4, "hoge");
    {
        shared_ptr<const C> cc = c;
        shared_ptr<int> v = c->get_val_ref();

        // &(cc->str) みたいに生のポインタを取らないこと
        shared_ptr<const string> cstr = util::mem_ptr(cc, &C::str);

        *v += 1;

        cout << base1->val << endl; // 1
        cout << *v << endl; // 5
        cout << cc->val << endl; // 5
        cout << *cstr << endl; // hoge
        cout << c.use_count() << endl; // 4
        
        auto f = cc->get_str_func();
        cout << f() << endl; // hoge
        cout << c.use_count() << endl; // 5
    }
    cout << c.use_count() << endl; // 1
    base1 = c;
    cout << base1->val << endl; // 5
    cout << c.use_count() << endl; // 2
    cout << *c->get_func()() << endl; // 5
    cout << c.use_count() << endl; // 2

    // publicなコンストラクタは無いはずなので実体化できない
    // Base2 _b2;
    // C _c(0, "");
    
    // コピーもムーブもさせない
    // C _tmp = *c;
    // C _tmp2 = std::move(*c);
    
    // 参照は取れる
    C &tmp = *c;
    C &&tmp2 = std::move(*c);
}
title tags author slide
C++ ラムダでPerfect Forwardキャプチャする方法メモ
C++ C++14 C++20
hikarin522
false

ラムダの初期化キャプチャで Perfect Forward しようとして以下の様にしてしまうとバグる

template <class T>
auto get_printer(T &&val) {
    return [val = std::forward<T>(val)]() {
        std::cout << val << std::endl;
    };
}
int a = 10;
auto printer = get_printer(a);
printer(); // 10
a = 20;
printer(); // 20 じゃなくて 10 が表示されてしまう???

これは T が左辺値参照型の時に参照キャプチャじゃなくコピーキャプチャしてしまってるのが問題

なので T が左辺値参照型の時は reference_wrapper で、その他の時は move してキャプチャするみたいなことが必要になる

で、そういう事をちゃんとしようとすると非常にめんどくさそうだが tuple 使えばいい感じにできる

template <class T>
auto get_printer(T &&val) {
    return [val = std::tuple<T>(std::forward<T>(val))]() {
        std::cout << std::get<0>(val) << std::endl;
    };
}

これで左辺値参照の時でも左辺値参照のままキャプチャできるようなる

そして可変長テンプレートにも簡単に対応できる

template <class ...T>
auto get_printer(T &&...val) {
    return [val = std::tuple<T...>(std::forward<T>(val)...)]() {
        boost::fusion::for_each(val, [](auto &&val) {
            std::cout << val << std::endl;
        });
    };
}

https://boostjp.github.io/tips/tuple.html

もっと簡単な書き方あったら教えて


C++20 でラムダ式の初期化キャプチャでパック展開できるようになるらしいが結局 forward するときは使えないのでくそ

https://cpprefjp.github.io/lang/cpp20/allow_pack_expansion_in_lambda_init_capture.html

title tags author slide
誤って削除したgit stashを復元する
Git
hikarin522
false

コマンドのエイリアス設定

.gitconfigに以下を追加

[alias]
stash-find = !"git fsck --unreachable | awk '/commit/ {print $3}' | xargs git log --merges --no-walk --grep=WIP --all-match"
stash-pick = cherry-pick -n -m1

使い方

# 消してしまった stash commit のハッシュを探す
git stash-find --oneline --grep=hoge

# 見つかったコミットを cherry-pick する
git stash-pick 'commit hash'

参考

https://qiita.com/y-ishizaka/items/8fbd88a63915ae31a809

title tags author slide
GitのSJIS対応メモ
Git VSCode
hikarin522
false

環境

OS: Windows7 Git: 2.15.0.windows.1 VS Code: 1.18.1 PSVersion: 5.1.14409.1012

git config

[core]
# 日本語ファイル名対応
    quotepath=false

# ページャの文字化け対応
    pager=LC_ALL=ja_JP.UTF-8 less -Sx4

# gitk, git-guiの設定
[gui]
    encoding=utf-8

# git diff, git show 時に文字コード変換
[diff "cp932"]
    textconv=iconv -f cp932 -t utf-8
[diff "sjis"]
    textconv=iconv -f sjis -t utf-8

VS Code

git show 時に --textconv のオプションが付いてない問題の対応 C:\Program Files\Microsoft VS Code\resources\app\extensions\git\out\git.js Repository::buffer(object, encoding = 'utf8') L413: const child = this.stream(['show', object]);const child = this.stream(['show', object, '--textconv']); に変更 https://github.com/Microsoft/vscode/blob/1.18.1/extensions/git/src/git.ts#L543

.gitattributes

# リポジトリ内のsjisファイルを指定
*.txt diff=sjis
*.c diff=cp932
*.h diff=cp932

未解決の問題

  • VS Codeのdiff
    VS Codeでのdiff表示時に全角チルダの変換がうまくいってない。
    iconvをnkfにしてもダメだった。

とりあえず git config を textconv = "iconv_cp932() { iconv -c -f cp932 -t utf-8 $1 | sed 's/〜/~'/g; }; iconv_cp932" にすると直るけどなんかあれ 😇
も追加 textconv = "iconv_cp932() { iconv -c -f cp932 -t utf-8 $1 | sed -e 's/〜/~/g' -e 's/−/-/g'; }; iconv_cp932"

  • git add -p
    git add -p 内部でのgit diffでiconvが呼ばれていない。
    $ git config --global interactive.diffFilter "iconv -f cp932 -t utf8"
    とすると直るがこれだと全てcp932になってしまう。

iconvじゃなくてnkfを使うと入力側文字コードを自動判定してくれるので
$ git config --global interactive.diffFilter "nkf -w" とするととりあえずはなんとかなる。
※ Git: 2.15.0.windows.1にnkfは付いてないので別で入れて下さい。

追記

最近sjisのファイル触らんくなったからあんま分からんけど .gitattributesworking-tree-encoding の設定するのがよさそう。 https://git-scm.com/docs/gitattributes#_code_working_tree_encoding_code

title tags author slide
C++11 で C++20 の bind_front 使えるようにする
C++ C++11 C++20 C++14
hikarin522
false

https://cpprefjp.github.io/reference/functional/bind_front.html

以下のヘッダをインクルードすれば使えるようになるはず

※ g++4.9 で確認

#pragma once
#include <functional>
#ifndef __cpp_lib_bind_front
namespace std {

// N は bind 後の残りの引数の数
// I は再帰用 (std::placeholders::_n の数)
template <size_t N, size_t I = 0>
struct BindFront
{
	template <typename ...T>
	static auto bind(T &&...args)
		-> decltype(BindFront<N, I + 1>::bind(std::forward<T>(args)..., std::_Placeholder<I + 1>{}))
	{
		// gcc は std::_Placeholder<n>{} で std::placeholders::_n が取れる
		// clang の場合は std::placeholders::__ph<n>{} で取れる
		// VC++ は std::_Ph<n>{}
		// 再帰で std::placeholders::_n を増やしていく
		return BindFront<N, I + 1>::bind(std::forward<T>(args)..., std::_Placeholder<I + 1>{});
	}
};

// 特殊化で N == I の時 std::bind に投げる
template <size_t N>
struct BindFront<N, N>
{
	template <typename ...T>
	static auto bind(T &&...args)
		-> decltype(std::bind(std::forward<T>(args)...))
	{
		return std::bind(std::forward<T>(args)...);
	}
};

// 以下 bind_front の定義

// const なメンバ関数用
template <typename TClass, typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(TRet(TClass::*func)(TArgs...) const, TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...);
}

// メンバ関数用
template <typename TClass, typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(TRet(TClass::*func)(TArgs...), TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...);
}

// 関数ポインタ用
template <typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(TRet(*func)(TArgs...), TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...);
}

// std::function 用
template <typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(const std::function<TRet(TArgs...)> &func, TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...);
}

// std::function&& 用
template <typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(std::function<TRet(TArgs...)> &&func, TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(std::move(func), std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(std::move(func), std::forward<TObj>(obj)...);
}

} // std
#endif // __cpp_lib_bind_front

いちいち戻り値用の decltype に同じの書かなあかんのめんどくさい

ちなみにC++14だとこうなる

https://gist.github.com/hikarin522/60ecf4b028f353d594e06f93f52ad3f8

はやくC++17あたりになりたい

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment