Skip to content

Instantly share code, notes, and snippets.

@cleoold
Last active July 4, 2020 10:24
Show Gist options
  • Save cleoold/c386a198583714e3816e3da932fc1c93 to your computer and use it in GitHub Desktop.
Save cleoold/c386a198583714e3816e3da932fc1c93 to your computer and use it in GitHub Desktop.
simple publisher-observer logic gate simulation
#include <iostream>
#include <vector>
#include <string>
// observer-based logic gate simulation
// things are done in a naive way. one may have stack overflow
// if events invoke each other
// interfaces
struct LUnit {
static long count;
std::string name;
LUnit() : name{std::string{"unnamed."} + std::to_string(count++)} {}
virtual void set(bool, int) {}
virtual ~LUnit() = 0;
};
long LUnit::count {0};
LUnit::~LUnit() {}
struct SingleOutputUnit : LUnit {
LUnit *to;
int to_serial;
virtual void wired(LUnit *to_, int s) {
to = to_;
to_serial = s;
}
virtual ~SingleOutputUnit() = 0;
};
SingleOutputUnit::~SingleOutputUnit() {}
struct MultiOutputUnit : LUnit {
std::vector<std::pair<LUnit *, int>> to;
virtual void wired(LUnit *to_, int to_port, int my_out_port) {
to[my_out_port] = { to_, to_port };
}
virtual ~MultiOutputUnit() = 0;
};
MultiOutputUnit::~MultiOutputUnit() {}
// simple logic gates implementations
struct Terminal : LUnit {
void set(bool b, int in_serial) override {
if (in_serial != 0) throw std::runtime_error {"invalid input port"};
std::cout << "gate: <" << name << "> value: <" << b << ">" << std::endl;
}
};
struct Wire : SingleOutputUnit {
void set(bool b, int in_serial) override {
if (!to) throw std::runtime_error {"wire disconnected"};
if (in_serial != 0) throw std::runtime_error {"invalid input port"};
to->set(b, to_serial);
}
};
struct NotGate : SingleOutputUnit {
void set(bool b, int in_serial) override {
if (!to) throw std::runtime_error {"wire disconnected"};
if (in_serial != 0) throw std::runtime_error {"invalid input port"};
to->set(!b, to_serial);
}
};
struct AndGate : SingleOutputUnit {
bool in1, in2;
bool in1set = false, in2set = false;
void set(bool b, int in_serial) override {
if (!to) throw std::runtime_error {"wire disconnected"};
if (in_serial == 0) in1 = b, in1set = true;
else if (in_serial == 1) in2 = b, in2set = true;
else throw std::runtime_error {"invalid input port"};
if (in1set && in2set) to->set(in1 && in2, to_serial);
}
};
struct OrGate : SingleOutputUnit {
bool in1, in2;
bool in1set = false, in2set = false;
void set(bool b, int in_serial) override {
if (!to) throw std::runtime_error {"wire disconnected"};
if (in_serial == 0) in1 = b, in1set = true;
else if (in_serial == 1) in2 = b, in2set = true;
else throw std::runtime_error {"invalid input port"};
if (in1set && in2set) to->set(in1 || in2, to_serial);
}
};
template<int size = 2>
struct Splitter : MultiOutputUnit {
Splitter() {
static_assert(size >= 2);
to.resize(size);
}
void wired(LUnit *to_, int to_port, int my_out_port) override {
if (static_cast<unsigned>(my_out_port) >= size)
throw std::runtime_error {"invalid output port"};
MultiOutputUnit::wired(to_, to_port, my_out_port);
}
void set(bool b, int in_serial) override {
if (in_serial != 0) throw std::runtime_error {"invalid input port"};
for (auto &t : to) {
if (!t.first) throw std::runtime_error {"wire disconnected"};
t.first->set(b, t.second);
}
}
};
// some compound logic gate implementations
struct NandGate : SingleOutputUnit {
AndGate andg;
NotGate notg;
NandGate() {
andg.wired(&notg, 0);
}
void wired(LUnit *to_, int s) override {
SingleOutputUnit::wired(to_, s);
notg.wired(to_, s);
}
void set(bool b, int in_serial) override {
andg.set(b, in_serial);
}
};
// A^B = (A+B)~(AB)
struct XorGate : SingleOutputUnit {
Splitter<2> ain, bin;
NandGate nandg;
OrGate org;
AndGate andg;
XorGate() {
ain.wired(&nandg, 0, 0);
ain.wired(&org, 0, 1);
bin.wired(&nandg, 1, 0);
bin.wired(&org, 1, 1);
nandg.wired(&andg, 0);
org.wired(&andg, 1);
}
void wired(LUnit *to_, int s) override {
SingleOutputUnit::wired(to_, s);
andg.wired(to_, s);
}
void set(bool b, int in_serial) override {
if (in_serial == 0) ain.set(b, 0);
else if (in_serial == 1) bin.set(b, 0);
else throw std::runtime_error {"invalid input port"};
}
};
// what the fk
template<int size>
struct MultiwayAndGate : MultiwayAndGate<size-1> {
AndGate anothergate;
MultiwayAndGate() {
MultiwayAndGate<size-1>::wired(&anothergate, 0);
}
void wired(LUnit *to_, int s) override {
SingleOutputUnit::wired(to_, s);
anothergate.wired(to_, s);
}
void set(bool b, int in_serial) override {
if (in_serial == size-1) anothergate.set(b, 1);
else MultiwayAndGate<size-1>::set(b, in_serial);
}
};
template<>
struct MultiwayAndGate<2> : SingleOutputUnit {
AndGate gate;
void wired(LUnit *to_, int s) override {
SingleOutputUnit::wired(to_, s);
gate.wired(to_, s);
}
void set(bool b, int in_serial) override {
gate.set(b, in_serial);
}
};
// non-member convenient functions
void wire(SingleOutputUnit *from, LUnit *to, int to_port) {
if (!from) throw std::runtime_error {"cannot wire"};
from->wired(to, to_port);
}
void wire(MultiOutputUnit *from, LUnit *to, int to_port, int from_output_port) {
if (!from) throw std::runtime_error {"cannot wire"};
from->wired(to, to_port, from_output_port);
}
void wire(LUnit *from, LUnit *to, int to_port) {
wire(dynamic_cast<SingleOutputUnit *>(from), to, to_port);
}
void wire(LUnit *from, LUnit *to, int to_port, int from_output_port) {
wire(dynamic_cast<MultiOutputUnit *>(from), to, to_port, from_output_port);
}
void nameit(LUnit *u, std::string n) {
u->name = std::move(n);
}
// demo
int main() {
LUnit *i1w = new Wire{},
*i2w = new Wire{},
*nandg = new NandGate{},
*spli = new Splitter<2>{},
*term = new Terminal{},
*notg = new NotGate{},
*term2 = new Terminal{};
wire(i1w, nandg, 0);
wire(i2w ,nandg, 1);
wire(nandg, spli, 0);
wire(spli, term, 0, 0);
wire(spli, notg, 0, 1);
wire(notg, term2, 0);
nameit(term, "nand");
i1w->set(true, 0);
i2w->set(true, 0);
LUnit *xorg = new XorGate{},
*xterm = new Terminal{};
wire(xorg, xterm, 0);
nameit(xterm, "xor");
xorg->set(false, 0);
xorg->set(false, 1);
LUnit *mandg = new MultiwayAndGate<5>{},
*mnotg = new NotGate{},
*mterm = new Terminal{};
wire(mnotg, mandg, 2);
wire(mandg, mterm, 0);
mandg->set(true, 0);
mandg->set(true, 1);
mnotg->set(false, 0);
mandg->set(true, 3);
mandg->set(true, 4);
// suddenly change
mandg->set(false, 4);
// delete
}
gate: <nand> value: <0>
gate: <unnamed.8> value: <1>
gate: <xor> value: <0>
gate: <unnamed.24> value: <1>
gate: <unnamed.24> value: <0>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment