なお、リモートデスクトップのサーバー側(接続される側)はWindowsProエディション以上である必要があるのでそこもご注意ください
¶まずWindowsPCを2台用意しLAN接続します。(この時点でもうハードルが高いでしょうか。すみません。)
¶(リモートデスクトップのサーバー側(接続される側)はWindowsProエディション以上である必要があるのでそこもご注意ください)
¶(リモートデスクトップのサーバー側(接続される側)はWindowsProエディション以上である必要があるのでそこもご注意ください)
¶たとえばデスクトップアプリ開発で、手元に4Kモニタはないけど4Kでどう表示されるのか確認したいとか、稀によくあると思います。
たとえばデスクトップアプリ開発で、手元に4Kモニタはないけど4Kでどう表示されるのか確認したいとか、稀によくあると思います。
そういう場合の小技です。
まずWindowsPCを2台用意しLAN接続します。(この時点でもうハードルが高いでしょうか。すみません。)
次に、片方のPCからもう片方のPCへリモートデスクトップで接続します。
その際、コマンドラインから
> %windir%\system32\mstsc.exe -w:3840 -h:2160
として起動し、そこから接続してください。4K画面としてリモートデスクトップが起動できます。
スマートサイズ指定ONにすれば縮小された全画面が見えますし、OFFなら等倍表示(スクロールビューで表示)されます。
以上、どなたかのお役に立てれば幸いです。
なお、リモートデスクトップのサーバー側(接続される側)はWindowsProエディション以上である必要があるのでそこもご注意ください
]]>・見てくれがダサい!(・・・個人の感想です)
¶Yコンビネータなる実装です。関数の引数で自分自身が与えられています。
コレの弱点としては
・あとは見てくれがダサい!(・・・個人の感想です)
・ラムダではないのでキャプチャが使えない!(これはイタイ)
・つまり無名関数ではない!(・・・なので記事タイトルに偽りアリとのご指摘あればスミマセン)
・あとは見てくれがダサい!(・・・でもない?)
・あとは見てくれがダサい(!・・・でもない?)
¶なお、以下サイトの記事 "C++のラムダで再帰する" はとても充実しており大変参考になりました。
その他、サイトで色々な情報を提供されている方々にも感謝です。
ありがとうございました。
無名関数(ラムダ)を再帰呼び出ししたくなることが稀によくあると思います。
そこで昨今ならば不動点コンビネータが定石かと思います。
int fibonacci(int n) { const auto Y = [](auto f) { return [=](auto... args) { return f(f, args...); }; }; auto f = Y([](auto f, int n)->int { return n < 2 ? n : f(f, n - 1) + f(f, n - 2); }); return f(n); } int main() { std::cout << fibonacci(10) << std::endl; }
Yコンビネータなる実装です。関数の引数で自分自身が与えられています。
これでまったく問題ありません。解決です。
がしかし唯一の弱点としては C++14 以上が必要になることです。
ですのでココでは C++14 未満でも使えるローテクな手段
その場にローカルクラス(構造体)と関数を定義し再帰呼び出し です。
int fibonacci(int n) { struct F { static int f(int n) { return n < 2 ? n : f(n - 1) + f(n - 2); } }; return F::f(n); }
コレの弱点としては
・ラムダではないのでキャプチャが使えない!(これはイタイ)
・つまり無名関数ではない!(・・・なので記事タイトルに偽りアリとのご指摘あればスミマセン)
・見てくれがダサい!(・・・個人の感想です)
などありますが、関数の外(スコープ)を汚すこともなく、
std::function も使わず(計ってはいませんが)遅くもなさそうで、
実はコレで十分な場合も多そうです。
ちなみに constexpr もイケます。
int rev(int n){ struct F { static constexpr int f(int n) { return n < 2 ? n : f(n - 1) + f(n - 2); } }; switch (n) { case F::f(3): return 3; case F::f(4): return 4; case F::f(5): return 5; } return -1; }
C++ は14や17になって色々と出来ることが増えてきましたが、
ローカルクラス自体は C++11 未満のコンパイラ、C++03 とかでも使えるハズなので有難く思うことがあるかもしれません。
なお、以下サイトの記事 "C++のラムダで再帰する" はとても充実しており大変参考になりました。
https://koturn.hatenablog.com/entry/2018/06/10/060000
その他、サイトで色々な情報を提供されている方々にも感謝です。
ありがとうございました。
ちなみに C++03 くらいのコンパイラでも RTTI がサポートされていれば同じこと(型がキーの std::map)が出来るので目新しい小技ではありません。がしかしココまでシンプルには書けません。
¶std::type_index は C++11 から使えますが、以下コードは C++17 での書式も使っています。
}
ちなみに C++03 くらいのコンパイラでも、RTTI がサポートされていれば同じこと(型がキーの std::map)が出来るので目新しい小技ではありません。がしかしココまでシンプルには書けません。
¶ちなみに C++03 くらいのコンパイラでも RTTI がサポートされていれば、ほぼ同じこと(型がキーの std::map)が出来るので目新しい小技ではありません。がしかしココまでシンプルには書けません。
¶ちなみに、C++03 くらいのコンパイラでも RTTI がサポートされていれば、ほぼ同じこと(型がキーの std::map)が出来るので目新しい小技ではありません。がしかしココまでシンプルには書けません。
変数の型によって処理を切り替えたいことが稀によくあるんじゃないかと思います。
C++ では RTTI なる情報で実行時に型を判断出来るので以下のようなコードが書けます。
struct Animal { virtual ~Animal() {} // ポリモーフィックにする }; struct Dog : public Animal {}; struct Cat : public Animal {}; void func(Animal* p) { if (typeid(*p) == typeid(Dog)) { // 型が Dog なら // ・・・ } else if (typeid(*p) == typeid(Cat)) { // 型が Cat なら // ・・・ } }
コンパイル時に型が判明する場合は if constexpr などもアリですが、本稿は実行時に判断する場合の話です。
これで十分事足りるのですが、もう少し粋に書きたい場合もあると思います。
if 文の羅列ではなく switch~case みたいな感じで。
がしかし switch~case で分岐できる式は整定数式のみです。
そこで、switch~case とまではいきませんがそれっぽい条件分岐として std::type_index をキーにした std::map を使った書き方です。
std::type_index は C++11 から使えますが、以下コードは C++17 での書式も使っています。
int main(void){ using namespace std; // クラス定義 struct Car { virtual ~Car() {} }; struct Nissan : public Car {}; struct Silvia : public Nissan {}; struct Skyline : public Nissan {}; struct Toyota : public Car {}; struct Chaser : public Toyota {}; const std::vector<std::shared_ptr<Car>> v = { // サンプル make_shared<Nissan>(), make_shared<Silvia>(), make_shared<Skyline>(), make_shared<Toyota>(), make_shared<Chaser>(), make_shared<Car>(), }; // それぞれの型に対応したコードを std::map で構築 // 注: static 変数であることが実行速度では有利です(この例では1度しか通らないのでアレですが・・) static const std::map<std::type_index, std::function<void(void)>> map = { {typeid(Nissan), [] { cout << "NISSAN" << endl; }}, {typeid(Silvia), [] { cout << "NISSAN SILVIA" << endl; }}, {typeid(Skyline), [] { cout << "NISSAN SKYLINE" << endl; }}, {typeid(Toyota), [] { cout << "TOYOTA" << endl; }}, {typeid(Chaser), [] { cout << "TOYOTA CHASER" << endl; }}, }; for (const auto i : v) { if (auto j = map.find(typeid(*i)); j != map.end()) { // mapから検索 j->second(); // 実行 } else { cout << "unknown" << endl; // 定義ナシ } } }
実行結果 NISSAN NISSAN SILVIA NISSAN SKYLINE TOYOTA TOYOTA CHASER unknown
この程度のボリュームの例でも、if 文の羅列よりは見通しもよいかなーと思います。
ちなみに C++03 くらいのコンパイラでも RTTI がサポートされていれば同じこと(型がキーの std::map)が出来るので目新しい小技ではありません。がしかしココまでシンプルには書けません。
C++は難しげな機能も沢山追加されていますが地味な改修もとても有難いです。
C++関係者の皆様には感謝です。
サーバーにアクセスしエラーだったらメールで通知します。それを cron で定期的に実行します。
¶# 'チェックするURL | エラー発生メールの宛先'
¶せっかくなので Docker Compose を使って最小限の記述をするだけで構築できる形にしています。
¶これまでは windows 上で同様のことをしておりました。特に不都合もなかったのですが環境自体もかなり古くなったこともありシェルスクリプトで作ってみました。
¶# wget でチェックし、200 以外はエラーとしています
サーバーの死活監視スクリプトです。
サーバーにアクセスしエラーだったらメールで通知します。それを cron で定期的に実行します。
というような簡素な機能しかありませんが事足りることも多いので。
これまでは windows 上で同様のことをしておりました。特に不都合もなかったのですが環境自体もかなり古くなったこともありシェルスクリプトで作ってみました。
せっかくなので Docker Compose を使って最小限の記述をするだけで構築できる形にしています。
いろいろなサイトの情報を参考にさせてもらっています。ありがとうございます。
version: '3' services: centos7: build: ./ volumes: - ./cron.sh:/opt/cron.sh environment: TZ: Asia/Tokyo container_name: "healthcheck-centos7" # restart: always
FROM centos:centos7 RUN yum -y update # wget mailx cron インストール RUN yum -y install wget mailx cronie # .mailrc (SMTP) # 環境に合わせて記述してください RUN echo $'\n\ #set smtp-use-starttls\n\ set smtp=smtp://mail.example.com:587\n\ set smtp-auth=login\n\ set smtp-auth-user=info@example.com\n\ set smtp-auth-password=○○○○○○○\n\ set from="info@example.com"\n\ set ssl-verify=ignore\n\ ' > /root/.mailrc # 15分毎に cron.sh を実行します CMD chmod 744 /opt/cron.sh \ ; (echo "*/15 * * * * /opt/cron.sh > /dev/null 2>&1") | crontab - \ ; (crond -n &) \ ; sleep infinity
#!/bin/bash items=( # 以下に監視対象を記述してください # 'チェックするURL | エラー発生メールの宛先' 'https://www.example.com/|rb20det@example.com' 'http://www2.example.com/|jzx80@example.com,ae86@example.com' ) for item in ${items[@]}; do IFS='|' read -r url mailto <<< "$item" # wget でチェックし、200 以外はエラーとしています result=`wget --spider -nv --timeout 30 -t 5 $url 2>&1` ok=`echo "$result" | grep -c '200 OK'` if [ $ok -eq 0 ]; then subject="failed. ${url}" echo $result | mailx -s "$subject" "$mailto" fi done
rlib-StringFormat
namespace rlib
using namespace rlib::string;
namespace trlib
using namespace trlib::string; auto tr = String::Format(_T("%s FC3S %s"),t0,t1); // tr = _T("マツダ FC3S 13B-T") vc の CString版
C++ だと文字列をフォーマットするコードって結構大変です。
C++11(14) になって色々な選択肢は増えたんですが、いまいち決定打に欠けると言いますか・・。
というわけで boost です。boost::format を使った文字列フォーマットです。
% で繋げることなく、可変引数テンプレートを使って printf 風に使えるようにしてみました。
boost::format の機能のおかげで、型安全で例外が発生することもないようにしています。
例えばアプリで処理失敗が発生したときに、アプリログを残す際に sprintf などを使ってフォーマットしてるような場合、その書式をミスっていたりすると、アプリが落ちたりしちゃいます。
レアケース(処理失敗)を考慮したコードを書いたつもりが、再現率が低く修正しにくい致命的なバグを生んでしまったりするので、できれば、こういう箇所は特に安全に書いておきたいなと思う次第。
というわけで、
printf 系の関数は(windows の CString::Format とかも)使わないほうが安全です。
がしかし printf 系の使いやすさは捨て難いです。慣れちゃってもいるし。
そんなわけで printf 風の書き方も出来てしまう boost::format はすごく便利です。
開発者の方に感謝しつつ使わせていただこうと思います。
C++17 を前提にコードを刷新いたしました。
と同時に、色々な型を展開して出力する機能を追加してみました。
基本的な使い方は変わってませんが、引数に渡せる型を景気よく増やしてみました。
std::pair std::tuple
std::unique_ptr std::shared_ptr std::optional
std::array std::vector std::list std::deque std::initializer_list
std::set std::multiset std::map std::multimap
これらが渡された場合は、その中身を出力します。
std::tuple や std::unique_ptr などは、まぁ使い勝手が良くなったかなと感じますが、std::vector や std::map などのコンテナまで対応してしまったのはやりすぎかもしれません。
このあたりは実際に使う場合には要調整かなと思います。
C++17 の if constexpr はとても便利だなと感じます。
その分、昨今のC++の発展っぷりに付いていくことは、自分程度のスペックだととても大変です。C++20 や 23 の話題もあるし・・。
そのため、わからないことはすぐにネットで答えを探しています。色々な情報を提供してくれている方々には感謝しかありません。
そしてここまで複雑な(と自分は思う) C++言語の、仕様決めをしている方々ももちろんですが、コンパイラを作っていらっしゃる方々には、畏敬の念を通り越して畏怖の念すら感じざるを得ません!
ありがとうございます。
#include <boost/format.hpp> namespace rlib { namespace string { namespace inner { template<class> struct IsArray : std::false_type {}; template<class T, std::size_t N> struct IsArray<std::array<T, N>> :std::true_type {}; template<class> struct IsVector : std::false_type {}; template<class T1, class T2> struct IsVector<std::vector<T1, T2>> :std::true_type {}; template<class> struct IsList : std::false_type {}; template<class T1, class T2> struct IsList<std::list<T1, T2>> :std::true_type {}; template<class> struct IsDeque : std::false_type {}; template<class T1, class T2> struct IsDeque<std::deque<T1, T2>> :std::true_type {}; template<class> struct IsInitializerList : std::false_type {}; template<class T> struct IsList<std::initializer_list<T>> :std::true_type {}; template<class> struct IsSet : std::false_type {}; template<class T1, class T2, class T3> struct IsSet<std::set<T1, T2, T3>> :std::true_type {}; template<class> struct IsMultiSet : std::false_type {}; template<class T1, class T2, class T3> struct IsMultiSet<std::multiset<T1, T2, T3>> :std::true_type {}; template<class> struct IsMap : std::false_type {}; template<class T1, class T2, class T3, class T4> struct IsMap<std::map<T1, T2, T3, T4>> :std::true_type {}; template<class> struct IsMultiMap : std::false_type {}; template<class T1, class T2, class T3, class T4> struct IsMultiMap<std::multimap<T1, T2, T3, T4>> :std::true_type {}; template<class> struct IsPair : std::false_type { }; template<class T1, class T2> struct IsPair<std::pair<T1,T2>> :std::true_type {}; template<class> struct IsUniquePtr : std::false_type {}; template<class T1, class T2> struct IsUniquePtr<std::unique_ptr<T1,T2>> :std::true_type {}; template<class> struct IsSharedPtr : std::false_type {}; template<class T> struct IsSharedPtr<std::shared_ptr<T>> :std::true_type {}; template<class> struct IsOptional : std::false_type {}; template<class T> struct IsOptional<std::optional<T>> :std::true_type {}; template<class> struct IsTuple : std::false_type {}; template<class... T> struct IsTuple<std::tuple<T...>> :std::true_type {}; template <class CharT, class Head, class... Tail> void f(boost::basic_format<CharT>&, Head&, Tail&&...); template <class CharT> void f(const boost::basic_format<CharT>&) {} template <std::size_t I, class CharT, class... T, class... Tail> void f(boost::basic_format<CharT>& format, const std::tuple<T...>& head, Tail&&... tail) { if constexpr (I < sizeof...(T)) { f<I + 1, CharT>(format % std::get<I>(head), head, tail...); } else { f<CharT>(format, tail...); } } template <class CharT, class Head, class... Tail> void f(boost::basic_format<CharT>& format, Head& head, Tail&&... tail) { using HeadT = std::remove_const_t<Head>; if constexpr (IsUniquePtr<HeadT>::value|| IsSharedPtr<HeadT>::value|| IsOptional<HeadT>::value) { if (head) { f<CharT>(format, *head, tail...); } else { f<CharT>(format % "(null)", tail...); } } else if constexpr (IsPair<HeadT>::value) { f<CharT>(format, head.first, head.second, tail...); } else if constexpr (IsTuple<HeadT>::value) { f<0, CharT>(format, head, tail...); } else if constexpr (IsArray<HeadT>::value || IsVector<HeadT>::value || IsList<HeadT>::value || IsDeque<HeadT>::value || IsInitializerList<HeadT>::value || IsSet<HeadT>::value || IsMultiSet<HeadT>::value || IsMap<HeadT>::value || IsMultiMap<HeadT>::value) { // 範囲ループ可の型か?っていう便利記述がもしあるなら知りたい for (const auto& i : head) { f<CharT>(format, i); } f<CharT>(format, tail...); #ifdef _MSC_VER } else if constexpr (std::is_same_v<HeadT, CStringA> || std::is_same_v<HeadT, CStringW>) { f<CharT>(format % std::basic_string<CharT>(head.GetString()), tail...); #endif } else { f<CharT>(format % head, tail...); } } } template <class CharT, class... Args> std::basic_string<CharT> format(const CharT* lpszFormat, Args&&... args) { boost::basic_format<CharT> format; format.exceptions(boost::io::no_error_bits); // 例外を発生させない format.parse(lpszFormat); try { inner::f<CharT>(format, args...); } catch (...) { } return format.str(); } template <class CharT, class... Args> std::basic_string<CharT> format(const std::basic_string<CharT>& s, Args&&... args) { return format<CharT>(s.c_str(), args...); } } }
#include "StringFormat.h" // ヘッダをインクルードするのみで使えます void main() { using namespace rlib::string; {// 普通の使い方 std::string r = format(u8"%s %dSX SR%dDE%s", u8"日産", 180, 20, "T"); // "日産 180SX SR20DET" } {// std::wstring も可です std::wstring r = format(L"BNR%d RB%dDE%s", 32, 26, L"TT"); // L"BNR32 RB26DETT" } {// 適してない型でも例外発生しません std::string r = format("%dSX %s", 240, L"日産"); // "240SX 00007FF742CC123C" } {// std::string をそのまま書けます。c_str() が不要なので便利。 const std::string a = u8"トヨタ"; std::string b = "1JZ-GTE"; std::string r = format("%s jzx90 %s", a, b); // "トヨタ jzx90 1JZ-GTE" } #ifdef _MSC_VER {// VisualStudio の CString CString t0(_T("マツダ")); const CString t1(_T("13B-T")); auto r = format(_T("%s FC3S %s"), t0, t1); // L"マツダ FC3S 13B-T" } #endif {// std::tuple std::pair は展開します。 std::tuple<std::string, int, double> a{ "RPS13",1998,11.8 }; const std::pair<std::string, int> b{ "FR", 1848000 }; auto r = format( // "形式:RPS13 排気量:1998cc 燃費:11.8km/l 駆動方式:FR 価格:1848000円" u8"形式:%s 排気量:%dcc 燃費:%.1fkm/l 駆動方式:%s 価格:%d円", a, b); } {// std::unique_ptr std::shared_ptr std::optional は中身を出力にします。空(無効)の場合は"(null)"を出力します std::unique_ptr<int> u = std::make_unique<int>(1); std::shared_ptr<int> s = std::make_shared<int>(2); std::optional<int> o(3); std::optional<int> e; auto r = format("%d,%d,%d,%d", u, s, o, e); // "1,2,3,(null)" } {// その他コンテナも展開します。(やりすぎかもしれません。素直にコンパイルエラーに振ってもよいかも。) auto r0 = format("%d,%d,%d", std::array<int, 3>{ 1, 2, 3 }); // "1,2,3" auto r1 = format("%d,%d,%d", std::vector<int>{4, 5, 6}); // "4,5,6" auto r2 = format("%d,%d,%d", std::list<int>{7, 8, 9}); // "7,8,9" auto r3 = format("%d,%d,%d", std::deque<int>{10, 11, 12}); // "10,11,12" auto r4 = format("%d,%d,%d", std::set<int>{3, 2, 1}); // "1,2,3" 取れる順で出力します(以下同様) auto r5 = format("%d,%d,%d", std::map<int, int>{ {1, 2}, { 3,4 }}); // "1,2,3" auto r6 = format("%d,%d,%d", std::multiset<int>{1, 2, 3}); // "1,2,3" auto r7 = format("%d,%d,%d", std::multimap<int, int>{ {1, 2}, { 3,4 }});// "1,2,3" } {// コンテナの中も再帰で展開するので以下みたいな型も可です std::map<int, std::pair<std::string, std::vector<int>>> m{ {1, {"a", {5,6,7}}}, {8, {"b", {9,10}}} }; auto r = format("%s,%s,%s,%s,%s,%s", m); // "1,a,5,6,7,8" } {// boost::format なので未対応の型はコンパイルエラーになってくれます std::function<void()> f; auto r = format("%s", f); // コンパイルエラー }
こちらのページを参考にし、手元で試した結果をシンプルにまとめました。ありがとうございます。
¶(以降の解説は root で行っていますので、必要に応じて sudo するなりして下さい。)
¶Ubuntu8.10 に VMwareServer1.0 をインストールする方法です。
どちらもかなり古いバージョンなのですが、野暮い用事のために試したところ、ちょっと躓いたので備忘録として残しておきます。
こちらのページを参考にし、手元で試した結果をシンプルにまとめました。ありがとうございます。
https://www.howtoforge.com/how-to-install-vmware-server-1.0.x-on-an-ubuntu-8.10-desktop
ubuntu-8.10-desktop-i386 + VMware-server-1.0.10-203137 で確認しています
> apt-get install linux-headers-`uname -r` build-essential xinetd
> cd $HOME > tar -xvf VMware-server-1.0.10-203137.tar.gz > cd vmware-server-distrib > ./vmware-install.pl
> cd $HOME > wget --no-check-certificate http://www.insecure.ws/warehouse/vmware-update-2.6.27-5.5.7-2.tar.gz > tar -xvf vmware-update-2.6.27-5.5.7-2.tar.gz > cd vmware-update-2.6.27-5.5.7-2 > ./runme.pl
2020-00-00T00:00:00.000+09:00| vthread-4| I125: Building module with command "/usr/bin/make -j2 -C /tmp/modconfig-b6MdgY/vmnet-only auto-build HEADER_DIR=/lib/modules/3.10.0-1127.19.1.el7.x86_64/build/include CC=/usr/bin/gcc IS_GCC_3=no" 2020-00-00T00:00:00.000+09:00| vthread-4| W115: Failed to build vmnet. Failed to execute the build command.
CentOS7 x86_64 (kernel 3.10.0) + VMware-Player-12.5.2 で確認しています。
¶→ VMware Kernel Module Updater も完了し、正しく起動できるハズ
¶情報を公開されておられてる方々には大変感謝です。
CentOS7 に VMwarePlayer12 をインストールする方法です。
どちらも旧バージョンなのでいまさら感がありますが、野暮い用事のために試したところ、ちょっと躓いたので備忘録として残しておきます。
CentOS7 x86_64 (kernel 3.10.0) + VMware-Player-12.5.2 で確認しています。
・ ・ 2020-00-00T00:00:00.000+09:00| vthread-4| I125: Building module with command "/usr/bin/make -j2 -C /tmp/modconfig-b6MdgY/vmnet-only auto-build HEADER_DIR=/lib/modules/3.10.0-1127.19.1.el7.x86_64/build/include CC=/usr/bin/gcc IS_GCC_3=no" 2020-00-00T00:00:00.000+09:00| vthread-4| W115: Failed to build vmnet. Failed to execute the build command.
> tar -xf /usr/lib/vmware/modules/source/vmnet.tar
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || \ + (defined(RHEL_RELEASE_CODE) && RHEL_RELEASE_CODE >= 0x0704)
> tar -cf /usr/lib/vmware/modules/source/vmnet.tar vmnet-only
less: (l: T, r: U) => boolean = (l: T, r: U) => l as any < r
less: (l: U, r: T) => boolean = (l: U, r: T) => l as any < r less0: (l: T, r: U) => boolean = (l: T, r: U) => l as any < r, less1: (l: U, r: T) => boolean = (l: U, r: T) => l as any < r
return lowerBound(list,value,(l,r)=>!less(r,l));
JavaScript には二分探索(バイナリサーチ)が標準で用意されてません。たぶん。
が、あれば便利だと思うので作ってみました。
// lower_bound function lowerBound<T, U>(list: T[], value: U, less: (l: T, r: U) => boolean = (l: T, r: U) => l as any < r ): number { let count = list.length; let first = 0; while (0 < count) { const count2 = count / 2 | 0; const mid = first + count2; if (less(list[mid], value)) { first = mid + 1; count -= count2 + 1; } else { count = count2; } } return first; } // upper_bound function upperBound<T, U>(list: T[], value: U, less: (l: U, r: T) => boolean = (l: U, r: T) => l as any < r ): number { return lowerBound(list,value,(l,r)=>!less(r,l)); } // binary_search // ・2つの比較関数というのがいささか冗長な気がしています。もう少しスマートな書き方がもしあれば教えてほしいです function binarySearch<T, U>(list: T[], value: U, less0: (l: T, r: U) => boolean = (l: T, r: U) => l as any < r, less1: (l: U, r: T) => boolean = (l: U, r: T) => l as any < r ): boolean { const first = lowerBound(list, value, less0); return first >= list.length ? false : !less1(value, list[first]); }
const sortedList = [-5, 0, 5, 10, 10, 11, 11, 13, 18]; // ソート済みの配列 const testValues = [-7, -5, 0, 1, 2, 10, 12, 13, 18, 19]; // テストケース for (const v of testValues) { const l = lowerBound(sortedList, v); const u = upperBound(sortedList, v); const b = binarySearch(sortedList, v); console.log(`${v} -> lowerBound=${l} : upperBound=${u} : binarySearch=${b}`); }
-7 -> lowerBound=0 : upperBound=0 : binarySearch=false -5 -> lowerBound=0 : upperBound=1 : binarySearch=true 0 -> lowerBound=1 : upperBound=2 : binarySearch=true 1 -> lowerBound=2 : upperBound=2 : binarySearch=false 2 -> lowerBound=2 : upperBound=2 : binarySearch=false 10 -> lowerBound=3 : upperBound=5 : binarySearch=true 12 -> lowerBound=7 : upperBound=7 : binarySearch=false 13 -> lowerBound=7 : upperBound=8 : binarySearch=true 18 -> lowerBound=8 : upperBound=9 : binarySearch=true 19 -> lowerBound=9 : upperBound=9 : binarySearch=false
証明書の設定などは Webサーバーコンテナの環境変数に記述するのが基本なようで、それ以外の方法を見つけられなかったです。ご存じの方おられましたら是非ご教示頂きたいです。
¶この用途ではかなりメジャーな jwilder/nginx-proxy と jrcs/letsencrypt-nginx-proxy-companion というdockerイメージを利用しています。
¶証明書の設定などは Webサーバーコンテナの環境変数に記述するのが基本なようで、それ以外の方法を見つけられなかったです。
¶この用途では一番メジャーっぽい jwilder/nginx-proxy と jrcs/letsencrypt-nginx-proxy-companion というdockerイメージを利用しています。
¶証明書の設定は Webサーバーコンテナの環境変数に記述するのが基本なようで、それ以外の方法を見つけられなかったです。
SSLアクセラレータ兼リバースプロキシの docker-compose です。
この用途ではかなりメジャーな jwilder/nginx-proxy と jrcs/letsencrypt-nginx-proxy-companion というdockerイメージを利用しています。
少しの設定を書くだけで Let's Encrypt の証明書の取得と更新を自動でやってくれるのでメンテフリー(と言うと大げさですが)な HTTPS の webサーバー環境が作れてしまいます。
スバラシイです。
というわけで自分も使ってみようと思たのですが、ちょっと困ったことが・・。
今回の自分のケースでは、webサーバーは既に別にあって、しかも Windows です。
jwilder/nginx-proxy、jrcs/letsencrypt-nginx-proxy-companion の場合、多分ですが、
Webサーバーは同じhostにdockerコンテナとして稼働していることが前提のように見受けられます。
証明書の設定などは Webサーバーコンテナの環境変数に記述するのが基本なようで、それ以外の方法を見つけられなかったです。ご存じの方おられましたら是非ご教示頂きたいです。
そこで、Webサーバーと思わせ実はリバースプロキシなコンテナを立てるという形にしてみました。
インターネット │ │Port443 │ ┌───│───── Linux PC ────────────────┐ │ │ │ │ ┌┴─────────┐ ┌────────────┐ │ │ │jwilder/nginx-proxy │ │jrcs/letsencrypt-・・ │ │ │ │SSLアクセラレータ │ │Let's Encrypt 取得/更新 │ │ │ │(リバースプロキシ)│ │ │ │ │ └┬─────────┘ └────────────┘ │ │ │ │ │ │Port80 │ │ │ │ │ ┌┴───────────────────┐ │ │ │nginx │ │ │ │Webサーバーと思わせ実はリバースプロキシ │ │ │ └┬───────────────────┘ │ │ │ │ └───│──────────────────────────┘ │ │Port80 │ ┌───│───── Windows PC ───────────────┐ │ │ │ │ ┌┴──────────┐ │ │ │Webサーバー IISとか │ │ │ └───────────┘ │ │ │ └──────────────────────────────┘
こんな感じで、見ての通り余計なプロキシが1つ挟まっています。
いささか非効率なのは否めませんが、一応期待通りに動作していることと、手間を少なくということで、割り切ってます。
version: '3' services: nginx-proxy: image: jwilder/nginx-proxy labels: - com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true ports: # Let's Encrypt が証明書を発行する際、名前解決後にポート80で確認に来るので 80も通しておく必要アリ - 80:80 - 443:443 volumes: - vhost:/etc/nginx/vhost.d - html:/usr/share/nginx/html - /var/run/docker.sock:/tmp/docker.sock:ro - ./certs:/etc/nginx/certs:ro - ./nginx-proxy/custom.conf:/etc/nginx/conf.d/custom.conf:ro restart: always letsencrypt: image: jrcs/letsencrypt-nginx-proxy-companion volumes: - vhost:/etc/nginx/vhost.d - html:/usr/share/nginx/html - /var/run/docker.sock:/var/run/docker.sock:ro - ./certs:/etc/nginx/certs:rw # environment: # - NGINX_PROXY_CONTAINER=nginx-proxy やり方が間違ってるんだと思うけど、 # - NGINX_DOCKER_GEN_CONTAINER=nginx-proxy うまく探し出してくれないっぽいのでこのやり方はナシ restart: always bridge-1: image: nginx:latest volumes: - ./bridge-1/default.conf:/etc/nginx/conf.d/default.conf - ./bridge-1:/var/www/html environment: - VIRTUAL_PORT=80 # ↓ 環境に合わせて変更のこと - VIRTUAL_HOST=example.com - LETSENCRYPT_HOST=example.com - LETSENCRYPT_EMAIL=info@example.com restart: always volumes: vhost: html:
# バージョン情報を非表示 server_tokens off; # クライアントリクエストボディ上限を無制限 client_max_body_size 0;
server { listen 0.0.0.0:80; server_name localhost; location / { # ↓ 環境に合わせて変更のこと proxy_pass http://192.168.1.100:80/; proxy_redirect off; proxy_set_header Host $http_host; # proxy_set_header X-Real-IP $remote_addr; 設定既なのでココでは設定しない proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 0; } }
@echo on
del openssl-1.1.1d.tar.gz
rd /s /q openssl-1.1.1d
cd ./openssl-1.1.1d
perl Configure VC-WIN32 --prefix=%CD%\x86 --openssldir=%CD%\ssl no-asm no-shared
nmake.exe install
cmd /k
}}
> cd c:\openssl\openssl-1.1.0g > perl Configure VC-WIN32 --prefix=C:\openssl\x86 --openssldir=C:\openssl\openssl-1.1.0g\ssl no-asm no-shared
> make > make install
setlocal cd /d %~dp0 @echo on del openssl-1.1.1d.tar.gz rd /s /q openssl-1.1.1d :: powershell -Command "Invoke-WebRequest -Uri https://www.openssl.org/source/openssl-1.1.1d.tar.gz -OutFile openssl-1.1.1d.tar.gz" wsl wget https://www.openssl.org/source/openssl-1.1.1d.tar.gz wsl tar -xvf openssl-1.1.1d.tar.gz cd ./openssl-1.1.1d perl Configure VC-WIN32 --prefix=%CD%\x86 --openssldir=%CD%\ssl no-asm no-shared nmake.exe install cmd /k
さすがに、「このプロジェクトでは VisualStudio2017 を使うので 2019 はインストールするべからず!」みたいな開発環境ルールは避けたいので、VisualStudio2019 がインストールされている環境でも期待通り MSBuild.exe を叩ける bat を用意したので備忘録兼ねて載せておきます。
¶もしもっと良い方法があればご助言頂けると幸いです。
¶もしもっと良い方法があればご助言頂ければ幸いです。
¶もしもっと良い方法があればご助言お願いします。
¶がしかし困ったことに MSBuild.exe の配置場所(パス)って固定ではありません。環境事にバラバラです。
で、それを解決する為に(かどうかはわかりませんが) VisualStudio では vswhere.exe っていうのがあり、それを叩けば MSBuild.exe のパスが取れるよ。というような記事を目にしたことがあります。
なので、vswhere.exe を叩いておくようにすれば VisualStudio をバージョンアップしてもちゃんと動くんだろうからコレを使っておこう。と安心していたんですが、VisualStudio2019 をインストールしたら、MSBuild.exe のパスが取れなくなってしまいました。
さすがに、「このプロジェクトでは VisualStudio2017 を使うので 2019 はインストールするべからず!」みたいな開発環境ルールは避けたいので、VisualStudio2019 がインストールされている環境でも期待通り MSBuild.exe を叩ける bat を用意したので、備忘録兼ねて載せておきます。
開発の現場では作業の敷居を上げないために、シンプルな開発環境構築手順ってとても大事だと思います。
外部ライブラリであっても、このサイトからダウンロードしてビルドして出来上がったファイルはココに置いてパスを通して・・・みたいなことを各開発者に強いるのはなるべく避けたい。
Clone(Checkout)した後に、このスクリプトだけ叩けばOK!くらいまでシンプルだと理想だなと思います。
昨今は色々な言語でパッケージ管理ツール的なものが一般的に使われてると思うのですが、C++ 界隈ではソースをダウンロードして自分で手作業でビルドするものが多々あり、windows の場合は VC のプロジェクトファイルをビルドすべしっていうものがけっこうあります。
そこで必要になるのが VC プロジェクトをコマンドラインでビルドするために必要な MSBuild.exe。
がしかし困ったことに MSBuild.exe の配置場所(パス)って固定ではありません。環境事にバラバラです。
で、それを解決する為に(かどうかはわかりませんが) VisualStudio では vswhere.exe っていうのがあり、それを叩けば MSBuild.exe のパスが取れるよ。というような記事を目にしたことがあります。
なので、vswhere.exe を叩いておくようにすれば VisualStudio をバージョンアップしてもちゃんと動くんだろうからコレを使っておこう。と安心していたんですが、VisualStudio2019 をインストールしたら、MSBuild.exe のパスが取れなくなってしまいました。
さすがに、「このプロジェクトでは VisualStudio2017 を使うので 2019 はインストールするべからず!」みたいな開発環境ルールは避けたいので、VisualStudio2019 がインストールされている環境でも期待通り MSBuild.exe を叩ける bat を用意したので備忘録兼ねて載せておきます。
ネット上の情報をかき集めた知識でこさえてみました。感謝です。
> MSBuild.bat c:\app\app.vcxproj
こんな感じで使うと、インストールされている最新の MSBuild.exe でビルドを行います。
VisualStudio2019、2017、2015 の環境までは確認しています。
:: MSBuild.exe %* @echo off setlocal :: cd /d %~dp0 set PF32=%ProgramFiles(x86)% if not exist "%PF32%" set PF32=%ProgramFiles% set VsWherePath="%PF32%\Microsoft Visual Studio\Installer\vswhere.exe" :: vs2019 or later for /f "usebackq tokens=*" %%i in (`%VsWherePath% -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe`) do ( if exist "%%i" ( set MSBuildPath="%%i" goto :run ) ) :: vs2017 or earlier for /f "usebackq tokens=1* delims=: " %%i in (`%VsWherePath% -latest -requires Microsoft.Component.MSBuild`) do ( if /i "%%i"=="installationPath" ( if exist "%%j\MSBuild\15.0\Bin\MSBuild.exe" ( set MSBuildPath="%%j\MSBuild\15.0\Bin\MSBuild.exe" goto :run ) if exist "%%j\MSBuild\14.0\Bin\MSBuild.exe" ( set MSBuildPath="%%j\MSBuild\14.0\Bin\MSBuild.exe" goto :run ) if exist "%%j\MSBuild\12.0\Bin\MSBuild.exe" ( set MSBuildPath="%%j\MSBuild\12.0\Bin\MSBuild.exe" goto :run ) ) ) :run if not exist %MSBuildPath% ( echo "MSBuild.exe not find" exit /b 1 ) echo on %MSBuildPath% %*
#recent(30)