動作を中断して状況を見たいが、ブレークポイントなどでプログラムそのものを中断すると出力コードも動かなくて困る。デバッグしたい処理をスレッドで動かし、セマフォを使うことで任意の場所で中断・再開できるらしい。
#include <iostream> #include <semaphore> #include <atomic> #include <thread>
class MyBreakPoint { std::binary_semaphore gate{ 0 }; // 許可は最大1に飽和 std::atomic<bool> run_free{ false }; // trueで連続実行 public: // 停止点: run_free=false のときだけ待つ void BreakPoint() { if (run_free.load(std::memory_order_acquire)) return; gate.acquire(); } // 次のBreakPointまで進める void ContinueToNextBreak() { gate.release(); } // 以後ブレーク無視 void RunFree() { run_free.store(true, std::memory_order_release); gate.release(); // 既に待っていれば抜ける } // 以後BreakPointで停止 void PauseAtBreak() { run_free.store(false, std::memory_order_release); // 残っている許可を掃き出し、次回は必ず止まる while (gate.try_acquire()) { /* drain */ } } };
void worker(MyBreakPoint& pauser) { for (int i = 0; i < 100; ++i) { // 処理本体 std::cout << i << std::endl; if (i % 5 == 0) { std::cout << "i % 5 == 0" << std::endl; pauser.BreakPoint(); // ブレークポイント } // 動作を見るための速度調節 std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::cout << "done" << std::endl; }
int main() { MyBreakPoint poser; std::thread th(worker, std::ref(poser)); // ユーザー入力でデバッグ操作 for (char c; std::cin >> c; ) { if (c == 'c') poser.ContinueToNextBreak();// 次にBreakPointが呼ばれるまで実行 else if (c == 'r') poser.RunFree();// 以後BreakPointを無視 else if (c == 'p') poser.PauseAtBreak();// 以後BreakPointが呼ばれたら止まる } th.join(); }
MFCを使う機会が増えそうになってきた。CStringで正規表現する方法を調べたがATL,MFCには(今はもう)無いらしく、C++標準のstd::regexを使うのが一番いいらしい。
// AfxMessageBox #include <afxwin.h> // CString #include <atlstr.h> #include <string> #include <regex> #ifdef _UNICODE using tregex = std::wregex; using tmatch = std::wcmatch; #else using tregex = std::regex; using tmatch = std::cmatch; #endif int main() { CString text = _T("Hello, 123 こんにちは"); #ifdef _UNICODE CString cset= _T(" (UNICODE)"); #else CString cset= _T(" (MULTIBYTE)"); #endif tregex re(_T("こん.*")); tmatch match; if (std::regex_search( LPCTSTR(text), match, re)) { int length = (int)(match[0].second - match[0].first); CString result(match[0].first, length); AfxMessageBox(result + cset); } }
自分で導入したvcpkg installをコマンドプロンプトから実行して以下のエラーに遭遇
error: Could not locate a manifest (vcpkg.json) above the current working directory.
This vcpkg distribution does not have a classic mode instance.
これは自分で導入したvcpkgを実行したつもりで、実はVisual Studioが管理しているvcpkgが実行された場合に起こるらしい。具体的には
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat
などでVisual Studioの環境を設定した場合、且つ、環境変数などで自前のvcpkgのパスを通していた場合に起こる可能性がある。
つまり、vcvars64.batが環境を整備しているときに、Visual Studio以外で導入したvcpkgの存在が確認できた時、これを有効にしてしまうとVisual Studioのvcpkgを使っているつもりなのに異なるvcpkgを操作してしまったということが起こり得てしまいまずいので、強制的にVS用のvcpkgに上書きされる。
この上書きはdoskeyというコマンドで行われるらしい。以下の確認用コマンドで、もし
の結果が
などであれば、vcpkgのパスが上書きされているので、以下を実行してこの設定を消す。
描画した頂点の番号や座標値などを表示したいとき用。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <Eigen/Dense> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkActor.h> #include <vtkPolyDataMapper.h> #include <vtkPolyData.h> #include <vtkPoints.h> #include <vtkCellArray.h> #include <vtkUnsignedCharArray.h> #include <vtkProperty.h> #include <vtkPointData.h> //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
// 座標群生製
std::vector<Eigen::Vector3d> make_cloud(int count) { std::vector<Eigen::Vector3d> cloud; for (size_t i = 0; i < count; i++) { double x = (double)rand() / (double)RAND_MAX; double y = (double)rand() / (double)RAND_MAX; double z = (double)rand() / (double)RAND_MAX; cloud.push_back(Eigen::Vector3d(x, y, z)); } return cloud; }
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// #include <vtkStringArray.h> #include <vtkLabeledDataMapper.h> #include <vtkActor2D.h> #include <vtkTextProperty.h>
// ラベルを表すPolyDataを作成 vtkSmartPointer<vtkPolyData> toLabel(const std::vector<Eigen::Vector3d>& data) { ////////////////////////// // auto points = vtkSmartPointer<vtkPoints>::New(); // インデックス文字列を作成 auto labels = vtkSmartPointer<vtkStringArray>::New(); labels->SetName("labels"); for (vtkIdType i = 0; i < data.size(); ++i) { std::string text = std::to_string(i); points->InsertNextPoint(data[i].data()); // 位置 labels->InsertNextValue(text.c_str()); // ラベル } auto polyData = vtkSmartPointer<vtkPolyData>::New(); polyData->SetPoints(points); polyData->GetPointData()->AddArray(labels); return polyData; }
// ラベル表示用のActorを作成 vtkSmartPointer<vtkActor2D> toLabelActor( vtkSmartPointer<vtkPolyData> poly, int fontSize = 20 ) { double color[3] = { 1.0, 1.0, 1.0 }; auto labelMapper = vtkSmartPointer<vtkLabeledDataMapper>::New(); labelMapper->SetInputData(poly); labelMapper->SetLabelModeToLabelFieldData(); labelMapper->SetFieldDataName("labels"); auto textProperty = labelMapper->GetLabelTextProperty(); textProperty->SetFontSize(fontSize); // フォントサイズ textProperty->SetColor(color); // 色 //textProperty->SetShadow(1); auto labelActor = vtkSmartPointer<vtkActor2D>::New(); labelActor->SetMapper(labelMapper); return labelActor; }
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// int main(int /*argc*/, char** /*argv*/) { auto renderer = vtkSmartPointer<vtkRenderer>::New(); auto cloud = make_cloud(10);// 座標群を作成 ////////////////////////////////////// // 座標にデータ番号を表示 auto textpoly = toLabel(cloud); vtkSmartPointer<vtkActor2D> textactor = toLabelActor(textpoly); renderer->AddActor(textactor);// ラベル表示 ////////////////////////////////////// renderer->ResetCamera(); auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); interactor->Start(); //イベントループへ入る return 0; }

VTK の MFC 対応 vtkMFCWindow は、文字セットがUnicodeの場合、Debugモードで実行中に以下で実行時エラーが発生する。
m_vtkMFCWindow = new vtkMFCWindow(this);
VTKのCMakeLists.txtを編集してこれを回避できるらしい。
VTKのソースコードをダウンロードしたら、以下のファイルを開く
VTK-9.5.1\GUISupport\MFC\CMakeLists.txt
CMakeLists.txt内、ファイル中央付近に以下のように追加をする。
# C runtime lib linkage and MFC lib linkage *MUST* match.
# If linking to C runtime static lib, link to MFC static lib.
# If linking to C runtime dll, link to MFC dll.
if (vtk_mfc_static)
set(CMAKE_MFC_FLAG 1)
else ()
set(CMAKE_MFC_FLAG 2)
endif ()
set(classes
vtkMFCWindow
)
vtk_module_add_module(VTK::GUISupportMFC
CLASSES ${classes})
vtk_add_test_mangling(VTK::GUISupportMFC)
### 追加 ###
vtk_module_definitions(VTK::GUISupportMFC PRIVATE _UNICODE UNICODE)
#############
if (vtk_mfc_static)
vtk_module_link(VTK::GUISupportMFC
PRIVATE
"debug;nafxcwd;optimized;nafxcw"
"debug;LIBCMTD;optimized;LIBCMT"
Uxtheme
windowscodecs)
else ()
vtk_module_definitions(VTK::GUISupportMFC
PRIVATE
_AFXDLL)
endif ()
別に詰まったわけではないし、公式からダウンロードしたもので事足りるのだけれど、一応成功事例を残しておく。
Window 11, Visual C++ 2022 x64
以下を設定
BUILD_SHARED_LIBS=ON
これをすると、WEBRTCが使えなくなるのでOFFにする
BUILD_WEBRTC=OFF
BUILD_WEBRTC_FROM_SOURCE=OFF
あと、/MDにしたいので
STATIC_WINDOWS_RUNTIME=OFF

ビルド結果は、以下+resources。DLLが少ないのは良いこと。
Release:
lib/Open3D.lib
lib/tbb12.lib
bin/Open3D.dll
bin/tbb12.dll
Debug:
lib/Open3D.lib
lib/tbb12_debug.lib
bin/Open3D.dll
bin/tbb12_debug.dll

#include <iostream> #include <vector> #include <filesystem> #include <regex> #include <unordered_map> struct PathList { std::vector<std::filesystem::path> directories; std::vector<std::filesystem::path> files; };
// 指定したパス内のファイル一覧を取得
PathList GetFileList(const std::filesystem::path& _path) { PathList result; if (!std::filesystem::exists(_path) || !std::filesystem::is_directory(_path)) { return result; // 存在しない場合は空を返す } for (const auto& entry : std::filesystem::directory_iterator(_path)) { if (entry.is_directory()) { result.directories.push_back(entry.path()); } else if (entry.is_regular_file()) { result.files.push_back(entry.path()); } } return result; }
// パスのファイル名からIDを取り出し、pair<size_t, path> の配列で返す std::vector<std::pair<size_t, std::filesystem::path>> GetFileIncdies(const std::vector<std::filesystem::path>& pathlist, const std::regex& pattern, std::vector<std::filesystem::path>* excep) { std::vector<std::pair<size_t, std::filesystem::path>> result; for (const auto& p : pathlist) { const std::string name = p.filename().string(); std::smatch m; if (std::regex_search(name, m, pattern)) { // パターンにマッチしたかどうか bool ok = false; size_t idx = 0; for (size_t i = 1; i < m.size(); ++i) { const std::string s = m[i].str(); if (s.empty()) continue; // すべて数字かを判定 const bool all_digits = std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c) != 0; }); if (!all_digits) continue; try { idx = static_cast<size_t>(std::stoull(s)); ok = true; break; } catch (...) { // stoull 失敗時は次のキャプチャを試す } } if (ok) { result.emplace_back(idx, p); } else if (excep) { excep->push_back(p); } } else { if (excep) excep->push_back(p); } } // インデックス昇順、同値ならパスの辞書順 std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) { if (a.first != b.first) return a.first < b.first; return a.second < b.second; }); return result; }
template<typename Tuple,typename Pair,int Index> std::unordered_map<size_t, Tuple> CreateDataSetList(std::unordered_map<size_t, Tuple>& datamap,std::vector<Tuple>& datasets, std::vector<Pair>& pairs) { for (const auto& p : pairs) { size_t idx = p.first; const auto& path = p.second; auto& tupleitem = datamap[idx]; std::get<Index>(tupleitem) = path; // Index番目にパスをセット } return datamap; }
// マップをIDでソートしたものを返す template<typename Tuple> std::vector<std::pair<size_t, Tuple>> SortByID(const std::unordered_map<size_t, Tuple>& byidmap) { std::vector<std::pair<size_t, Tuple>> sorted; sorted.reserve(byidmap.size()); for (const auto& kv : byidmap) { sorted.emplace_back(kv.first, kv.second); } std::sort(sorted.begin(), sorted.end(), [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; // size_tキーで昇順 }); return sorted; }
using Tuple = std::tuple<std::filesystem::path, std::filesystem::path, std::filesystem::path>; using Pair = std::pair<size_t, std::filesystem::path>;
// パスとパターンからファイル一覧を作成し、インデックスとパスのペアを返す関数 std::vector<std::pair<size_t, std::filesystem::path> > GetFiles(const std::filesystem::path& path,std::regex pattern){ PathList fileList = GetFileList(path); std::vector<std::pair<size_t, std::filesystem::path> > flist; flist = GetFileIncdies(fileList.files, pattern, nullptr); return flist; }
int main() { std::vector< Tuple > dataSetList; std::vector<Pair > list_1 = GetFiles("C:\\test\\tmp\\txt", std::regex(R"(新規 テキスト000_(\d+)\.txt)") ); std::vector<Pair > list_2 = GetFiles("C:\\test\\tmp\\doc", std::regex(R"(doc_000_(\d+)\.doc)") ); std::unordered_map<size_t, Tuple> byidmap; CreateDataSetList<Tuple, Pair, 0>(byidmap, dataSetList, list_1); CreateDataSetList<Tuple, Pair, 1>(byidmap, dataSetList, list_2); std::vector<std::pair<size_t, Tuple>> sorted = SortByID(byidmap); for(const auto& b : sorted) { std::cout << "ID: " << b.first << " " << std::get<0>(b.second).filename() << " " << std::get<1>(b.second).filename() << " " << std::get<2>(b.second) << "\n"; } }
nlohmann::jsonによる書き込みを行う。書き込み順は記述順と同じでなくなる点に注意。
#include <iostream> #include <nlohmann/json.hpp> #include <fstream> int main() { nlohmann::json jsondata;
jsondata["information"] = { {"Name", "Tarou"}, {"Age", 18}, {"weight", 60.5} }; nlohmann::json& data = jsondata["data"]; data["dates"].push_back("2023-10-01T12:00:00+09:00"); data["dates"].push_back("2023-10-02T12:00:00+09:00"); nlohmann::json countries = { {{"id", 1}, {"name", "Russia"}}, {{"id", 2}, {"name", "France"}} }; data["countries"] = countries;
// JSONデータをファイルに保存
std::string filename = R"(C:\test\data\data2.json)";
std::ofstream file(filename);
if (file.is_open()) {
file << jsondata.dump(2); // インデントのスペース数
file.close();
std::cout << "JSON data saved to " << filename << std::endl;
} else {
std::cerr << "Failed to open file for writing: " << filename << std::endl;
}
return 0;
}
{
"data": {
"countries": [
{
"id": 1,
"name": "Russia"
},
{
"id": 2,
"name": "France"
}
],
"dates": [
"2023-10-01T12:00:00+09:00",
"2023-10-02T12:00:00+09:00"
]
},
"information": {
"Age": 18,
"Name": "Tarou",
"weight": 60.5
}
}
nlohmann::jsonの使用例を殆どやっていなかったことに気づいたのでここに置いておく。
#include <iostream> #include <nlohmann/json.hpp> #include <fstream> int main() { // json形式の文字列 std::string jsontext = R"( { "information":{ "Name":"Tarou", "Age":18, "weight":60.5 }, "data":{ "dates":[ "2023-10-01T12:00:00+09:00", "2023-10-02T12:00:00+09:00" ], "countries":[ { "id":1, "name":"Russia" }, { "id":2, "name":"France" } ] } } )"; //std::string filename = R"(C:\test\data\data.json)"; //std::ifstream file(filename); //nlohmann::json jsondata = nlohmann::json::parse(file); nlohmann::json jsondata = nlohmann::json::parse(jsontext); { nlohmann::json information = jsondata["information"]; // データの取得 std::string name = information["Name"]; int age = information["Age"]; double weight = information["weight"]; std::cout << "Name: " << name << std::endl; std::cout << "Age: " << age << std::endl; std::cout << "Weight: " << weight << std::endl; } { nlohmann::json data = jsondata["data"]; { nlohmann::json dates = data["dates"]; for (const auto& date : dates) { std::string date_str = date.get<std::string>(); std::cout << "Date: " << date_str << std::endl; } } { nlohmann::json countries = data["countries"]; for (const auto& country : countries) { int id = country["id"]; std::string name = country["name"]; std::cout << "Country ID: " << id << ", Name: " << name << std::endl; } } } }
構造体で受け取ることもできるが、あらかじめfrom_json関数を定義しておく必要がある
struct Country { int id; std::string name; };
// .get関数を使用するための定義 void from_json(const nlohmann::json& j, Country& p) { j.at("id").get_to(p.id); j.at("name").get_to(p.name); }
int main() { std::string filename = R"(C:\test\data\data.json)"; std::ifstream file(filename); nlohmann::json jsondata = nlohmann::json::parse(file); nlohmann::json data = jsondata["data"]; { std::vector<std::string> vectors = data["dates"]; for (const auto& date : vectors) { std::cout << "Date from vector: " << date << std::endl; } } { std::vector<Country> countries = data["countries"].get<std::vector<Country> >(); for (const auto& country : countries) { std::cout << "Country ID: " << country.id << ", Name: " << country.name << std::endl; } } }
#include <iostream> #include <string> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/depth_first_search.hpp> // .dotファイルの出力用 #include <boost/graph/graphviz.hpp>
// Vertexクラス // Boost Graph Libraryでは node ではなく vertex という用語を使用 struct VertexProperty { std::string name; };
// グラフ定義 using Graph = boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, //単方向グラフ VertexProperty >;
using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
// 深さ優先探索 class DFSVisitor : public boost::default_dfs_visitor { public: void discover_vertex(Vertex v, const Graph& g) const { std::cout << "Visited: " << g[v].name << std::endl; } };
int main() { Graph mygraph; // 頂点の追加 Vertex A = boost::add_vertex({ "A" }, mygraph); Vertex B = boost::add_vertex({ "B" }, mygraph); Vertex C = boost::add_vertex({ "C" }, mygraph); Vertex D = boost::add_vertex({ "D" }, mygraph); Vertex E = boost::add_vertex({ "E" }, mygraph); boost::add_edge(A, B, mygraph); // A→B boost::add_edge(A, C, mygraph); // A→C boost::add_edge(C, D, mygraph); // C→D boost::add_edge(C, E, mygraph); // C→E // 深さ優先探索 DFSVisitor vis; boost::depth_first_search(mygraph, boost::visitor(vis).root_vertex(A)); // DOTファイルに出力 std::ofstream ofs("graph.dot"); write_graphviz(ofs, mygraph, [&](std::ostream& out, const Vertex v) { out << "[label=\"" << mygraph[v].name << "\"]"; } ); // dot.exe -Tpng graph.dot -o a.png return 0; }
