Visitor pattern
Use case
Process different instruction types in a disassembler without modifying each instruction class.
Explanation
The visitor pattern separates algorithms from the objects they operate on. Modern C++ uses std::variant + std::visit.
Code
#include <cstdint>
#include <print>
#include <string>
#include <variant>
#include <vector>
// https://developer.arm.com/documentation/ddi0602/2025-12/Base-Instructions/MOV--register---Move-register-value--an-alias-of-ORR--shifted-register--?lang=en
struct MovInstr {
std::string dest;
std::string src;
};
// https://developer.arm.com/documentation/ddi0602/2025-12/Base-Instructions/BL--Branch-with-link-?lang=en
struct BlInstr {
uint64_t target;
std::string name;
};
// https://developer.arm.com/documentation/ddi0602/2025-12/Base-Instructions/RET--Return-from-subroutine-?lang=en
struct RetInstr {};
using Instruction = std::variant<MovInstr, BlInstr, RetInstr>;
struct DisasmVisitor {
void operator()(const MovInstr &m) const {
std::println("mov {}, {}", m.dest, m.src);
}
void operator()(const BlInstr &b) const {
std::println("bl {:#x} ; {}", b.target, b.name);
}
void operator()(const RetInstr &) const { std::println("ret"); }
};
int main() {
std::vector<Instruction> code = {MovInstr{"x0", "x1"},
BlInstr{0x10001000, "_printf"}, RetInstr{}};
std::println("Disassembly:");
for (const auto &instr : code) {
std::visit(DisasmVisitor{}, instr);
}
return 0;
}
Output
$ ./src/design-patterns-and-idioms/build/visitor-pattern
Disassembly:
mov x0, x1
bl 0x10001000 ; _printf
ret