Skip to content

Instantly share code, notes, and snippets.

@nikki93
Created December 30, 2022 19:50
Show Gist options
  • Save nikki93/458852c50cd4822f2c9935ce0d41a2bc to your computer and use it in GitHub Desktop.
Save nikki93/458852c50cd4822f2c9935ce0d41a2bc to your computer and use it in GitHub Desktop.
#include <string.h>
#include "rect.hh"
#include "sum_fields.hh"
#include "person/person.hh"
#include "gx.hh"
//
// Types
//
struct Point;
struct Before;
struct Inner;
struct Outer;
struct PtrPtr;
template<typename T>
struct Holder;
struct HasArray;
template<typename T>
using Seq = gx::Slice<T>;
struct SingleIncr;
struct DoubleIncr;
using Enum = int;
struct Nums;
struct HasDefaults;
struct HasString;
struct Foo;
struct Bar;
struct Point {
float x;
float y;
};
struct Before {
Point p;
};
struct Inner {
int z;
};
struct Outer {
int x;
int y;
Inner inner;
};
struct PtrPtr {
int **pp;
};
template<typename T>
struct Holder {
T Item;
};
struct HasArray {
gx::Array<int, 4> arr;
};
struct SingleIncr {
int val;
};
struct DoubleIncr {
int val;
};
struct Nums {
int A;
int B;
int C;
int D;
};
struct HasDefaults {
int foo = 42;
float bar = 6.4;
Point point = { 1, 2 };
};
struct HasString {
gx::String s;
};
struct Foo {
int val;
};
struct Bar {
int X;
int Y;
};
//
// Meta
//
inline void forEachField(Point &val, auto &&func) {
}
inline void forEachField(Before &val, auto &&func) {
}
inline void forEachField(Inner &val, auto &&func) {
}
inline void forEachField(Outer &val, auto &&func) {
}
inline void forEachField(PtrPtr &val, auto &&func) {
}
template<typename T>
struct gx::FieldTag<Holder<T>, 0> {
inline static constexpr gx::FieldAttribs attribs { .name = "item" };
};
template<typename T>
inline void forEachField(Holder<T> &val, auto &&func) {
func(gx::FieldTag<Holder<T>, 0>(), val.Item);
}
inline void forEachField(HasArray &val, auto &&func) {
}
inline void forEachField(SingleIncr &val, auto &&func) {
}
inline void forEachField(DoubleIncr &val, auto &&func) {
}
template<>
struct gx::FieldTag<Nums, 0> {
inline static constexpr gx::FieldAttribs attribs { .name = "a" };
};
template<>
struct gx::FieldTag<Nums, 1> {
inline static constexpr gx::FieldAttribs attribs { .name = "b" };
};
template<>
struct gx::FieldTag<Nums, 2> {
inline static constexpr gx::FieldAttribs attribs { .name = "c" };
};
template<>
struct gx::FieldTag<Nums, 3> {
inline static constexpr gx::FieldAttribs attribs { .name = "d", .twice = true };
};
inline void forEachField(Nums &val, auto &&func) {
func(gx::FieldTag<Nums, 0>(), val.A);
func(gx::FieldTag<Nums, 1>(), val.B);
func(gx::FieldTag<Nums, 2>(), val.C);
func(gx::FieldTag<Nums, 3>(), val.D);
}
inline void forEachField(HasDefaults &val, auto &&func) {
}
inline void forEachField(HasString &val, auto &&func) {
}
inline void forEachField(Foo &val, auto &&func) {
}
template<>
struct gx::FieldTag<Bar, 0> {
inline static constexpr gx::FieldAttribs attribs { .name = "x" };
};
template<>
struct gx::FieldTag<Bar, 1> {
inline static constexpr gx::FieldAttribs attribs { .name = "y" };
};
inline void forEachField(Bar &val, auto &&func) {
func(gx::FieldTag<Bar, 0>(), val.X);
func(gx::FieldTag<Bar, 1>(), val.Y);
}
//
// Function declarations
//
int fib(int n);
void testFib();
void testUnary();
void testVariables();
void testIncDec();
void testIf();
void testFor();
void setToFortyTwo(int *ptr);
void testPointer();
int outerSum(Outer o);
void setXToFortyTwo(Outer *o);
void testStruct();
float sum(Point p);
void setZero(Point *p);
void testMethod();
template<typename T>
T add(T a, T b);
template<typename T>
void incrHolder(Holder<T> *h);
template<typename T>
T get(Holder<T> h);
template<typename T>
void set(Holder<T> *h, T Item);
void testGenerics();
void iterateOneToTen(auto &&f);
void testLambdas();
void setSecondElementToThree(gx::Array<int, 4> *arr);
void testArrays();
void appendFortyTwo(gx::Slice<int> *s);
void testSlices();
template<typename T>
int len(Seq<T> *s);
template<typename T>
void add(Seq<T> *s, T val);
template<typename T, typename PT>
void incrSeq(Seq<T> *s);
void incr(SingleIncr *s);
void incr(DoubleIncr *s);
void testSeqs();
void setGlobalXToFortyTwo();
void checkGlobalXIsFortyTwo();
int three();
bool isGlobalSliceEmpty();
int apply(int val, auto &&fn);
void testGlobalVariables();
void testImports();
void testExterns();
void testConversions();
void testMeta();
void testDefaults();
void testStrings();
int main();
void check(bool val);
int Val(Foo *f);
Foo NewFoo(int val);
//
// Variables
//
constexpr int initialGlobalX = 23;
int globalX = initialGlobalX;
int globalZ = 14;
int globalW = globalX + 42;
int globalY = globalX - three();
gx::Slice<int> globalSlice;
int globalApplied = apply(3, [](int i) {
return 2 * i;
});
constexpr Enum ZeroEnum = 0;
constexpr int OneEnum = 1;
constexpr int TwoEnum = 2;
//
// Function definitions
//
int fib(int n) {
if (n <= 1) {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
}
void testFib() {
check(fib(6) == 8);
}
void testUnary() {
check(-(3) == -3);
check(+(3) == 3);
}
void testVariables() {
auto x = 3;
auto y = 4;
check(x == 3);
check(y == 4);
y = y + 2;
x = x + 1;
check(y == 6);
check(x == 4);
y += 2;
x += 1;
check(y == 8);
check(x == 5);
}
void testIncDec() {
auto x = 0;
(x)++;
check(x == 1);
(x)--;
check(x == 0);
}
void testIf() {
auto x = 0;
if (auto cond = false; cond) {
x = 2;
}
check(x == 0);
}
void testFor() {
{
auto sum = 0;
for (auto i = 0; i < 5; (i)++) {
sum += i;
}
check(sum == 10);
}
{
auto sum = 0;
auto i = 0;
for (; i < 5; ) {
sum += i;
(i)++;
}
check(sum == 10);
}
{
auto sum = 0;
auto i = 0;
for (; ; ) {
if (i >= 5) {
break;
}
sum += i;
(i)++;
}
check(sum == 10);
}
}
void setToFortyTwo(int *ptr) {
gx::deref(ptr) = 42;
}
void testPointer() {
auto val = 42;
check(val == 42);
auto ptr = &val;
gx::deref(ptr) = 14;
check(val == 14);
setToFortyTwo(ptr);
check(val == 42);
}
int outerSum(Outer o) {
return o.x + o.y + o.inner.z;
}
void setXToFortyTwo(Outer *o) {
gx::deref(o).x = 42;
}
void testStruct() {
{
auto s = Outer {};
check(s.x == 0);
check(s.y == 0);
check(s.inner.z == 0);
{
auto p = &s;
gx::deref(p).x = 2;
check(gx::deref(p).x == 2);
check(s.x == 2);
s.y = 4;
check(gx::deref(p).y == 4);
}
check(outerSum(s) == 6);
setXToFortyTwo(&s);
check(s.x == 42);
}
{
auto s = Outer { 2, 3, Inner { 4 } };
check(s.x == 2);
check(s.y == 3);
check(s.inner.z == 4);
s.x += 1;
s.y += 1;
s.inner.z += 1;
check(s.x == 3);
check(s.y == 4);
check(s.inner.z == 5);
}
{
auto s = Outer { .x = 2, .y = 3, .inner = Inner { .z = 4 } };
check(s.x == 2);
check(s.y == 3);
check(s.inner.z == 4);
}
{
auto s = Outer {
.x = 2,
.y = 3,
.inner = Inner {
.z = 4,
},
};
check(s.x == 2);
check(s.y == 3);
check(s.inner.z == 4);
}
{
}
{
auto i = 42;
auto p = &i;
auto pp = &p;
auto d = PtrPtr { pp };
gx::deref(gx::deref(d.pp)) = 14;
check(d.pp != nullptr);
check(i == 14);
}
{
auto p = PtrPtr {};
check(p.pp == nullptr);
}
}
float sum(Point p) {
return p.x + p.y;
}
void setZero(Point *p) {
gx::deref(p).x = 0;
gx::deref(p).y = 0;
}
void testMethod() {
auto p = Point { 2, 3 };
check(sum(p) == 5);
auto ptr = &p;
check(sum(gx::deref(ptr)) == 5);
setZero(&(p));
check(p.x == 0);
check(p.y == 0);
}
template<typename T>
T add(T a, T b) {
return a + b;
}
template<typename T>
void incrHolder(Holder<T> *h) {
gx::deref(h).Item += 1;
}
template<typename T>
T get(Holder<T> h) {
return h.Item;
}
template<typename T>
void set(Holder<T> *h, T Item) {
gx::deref(h).Item = Item;
}
void testGenerics() {
{
check(add<int>(1, 2) == 3);
check(add<float>(1.2f, 2.0f) == 3.2f);
check(add<float>(1.2f, 2.0f) == 3.2f);
}
{
auto i = Holder<int> { 42 };
check(i.Item == 42);
incrHolder<int>(&i);
check(i.Item == 43);
auto f = Holder<float> { 42 };
check(f.Item == 42);
check(add<float>(f.Item, 20) == 62);
incrHolder<float>(&f);
check(f.Item == 43);
auto p = Holder<Point> { Point { 1, 2 } };
check(p.Item.x == 1);
check(p.Item.y == 2);
setZero(&(p.Item));
check(p.Item.x == 0);
check(p.Item.y == 0);
set(&(p), Point { 3, 2 });
check(p.Item.x == 3);
check(p.Item.y == 2);
check(get(p).x == 3);
check(get(p).y == 2);
}
}
void iterateOneToTen(auto &&f) {
for (auto i = 1; i <= 10; (i)++) {
f(i);
}
}
void testLambdas() {
{
auto val = 42;
check(val == 42);
auto foo = [&](int newVal) {
val = newVal;
};
foo(14);
check(val == 14);
auto val2 = [&]() {
return val;
}();
check(val2 == val);
}
{
auto sum = 0;
iterateOneToTen([&](int i) {
sum += i;
});
check(sum == 55);
}
}
void setSecondElementToThree(gx::Array<int, 4> *arr) {
gx::deref(arr)[1] = 3;
}
void testArrays() {
{
auto arr = gx::Array<int, 4> { 1, 2, 3, 4 };
check(arr[2] == 3);
auto sum = 0;
for (auto i = 0; i < gx::len(arr); (i)++) {
sum += arr[i];
}
check(sum == 10);
check(arr[1] == 2);
setSecondElementToThree(&arr);
check(arr[1] == 3);
}
{
auto stuff = gx::Array<int, 3> { 1, 2, 3 };
check(gx::len(stuff) == 3);
auto sum = 0;
for (auto i = -1; auto &elem : stuff) {
++i;
check(i + 1 == elem);
sum += elem;
}
check(sum == 6);
}
{
auto arr = gx::Array<gx::Array<int, 2>, 2> { gx::Array<int, 2> { 1, 2 }, gx::Array<int, 2> { 3, 4 } };
check(gx::len(arr) == 2);
check(arr[0][0] == 1);
check(arr[0][1] == 2);
check(arr[1][0] == 3);
check(arr[1][1] == 4);
}
{
auto h = HasArray {};
check(gx::len(h.arr) == 4);
check(h.arr[0] == 0);
check(h.arr[1] == 0);
check(h.arr[2] == 0);
check(h.arr[3] == 0);
}
{
auto h = HasArray { gx::Array<int, 4> { 1, 2, 3, 4 } };
check(gx::len(h.arr) == 4);
check(h.arr[2] == 3);
}
}
void appendFortyTwo(gx::Slice<int> *s) {
gx::deref(s) = gx::append(gx::deref(s), 42);
}
void testSlices() {
{
auto s = gx::Slice<int> {};
check(gx::len(s) == 0);
s = gx::append(s, 1);
s = gx::append(s, 2);
check(gx::len(s) == 2);
check(s[0] == 1);
check(s[1] == 2);
appendFortyTwo(&s);
check(gx::len(s) == 3);
check(s[2] == 42);
}
{
auto s = gx::Slice<gx::Slice<int>> { gx::Slice<int> { 1 }, gx::Slice<int> {}, gx::Slice<int> { 3, 4 } };
check(gx::len(s) == 3);
check(gx::len(s[0]) == 1);
check(s[0][0] == 1);
check(gx::len(s[1]) == 0);
check(gx::len(s[2]) == 2);
check(s[2][0] == 3);
check(s[2][1] == 4);
}
{
auto stuff = gx::Slice<int> { 1, 2 };
stuff = gx::append(stuff, 3);
check(gx::len(stuff) == 3);
{
auto sum = 0;
for (auto i = -1; auto &elem : stuff) {
++i;
check(i + 1 == elem);
sum += elem;
}
check(sum == 6);
}
{
auto sum = 0;
for (auto i = -1; auto &_ [[maybe_unused]] : stuff) {
++i;
sum += i;
}
check(sum == 3);
}
{
auto sum = 0;
for (auto &elem : stuff) {
sum += elem;
}
check(sum == 6);
}
{
auto count = 0;
for (auto &_ [[maybe_unused]] : stuff) {
count += 1;
}
check(count == 3);
}
{
stuff = gx::Slice<int> {};
auto count = 0;
for (auto &_ [[maybe_unused]] : stuff) {
count += 1;
}
check(count == 0);
}
}
}
template<typename T>
int len(Seq<T> *s) {
return gx::len(gx::deref(s));
}
template<typename T>
void add(Seq<T> *s, T val) {
gx::deref(s) = gx::append(gx::deref(s), val);
}
template<typename T, typename PT>
void incrSeq(Seq<T> *s) {
for (auto i = -1; auto &_ [[maybe_unused]] : gx::deref(s)) {
++i;
incr(PT(&(gx::deref(s))[i]));
}
}
void incr(SingleIncr *s) {
gx::deref(s).val += 1;
}
void incr(DoubleIncr *s) {
gx::deref(s).val += 2;
}
void testSeqs() {
{
auto s = Seq<int> {};
check(len(&(s)) == 0);
add(&(s), 1);
add(&(s), 2);
check(len(&(s)) == 2);
check(s[0] == 1);
check(s[1] == 2);
}
{
auto s = Seq<int> { 1, 2, 3 };
check(len(&(s)) == 3);
auto sum = 0;
for (auto i = -1; auto &elem : s) {
++i;
check(i + 1 == elem);
sum += elem;
}
check(sum == 6);
}
{
auto s = Seq<Point> { Point { 1, 2 }, Point { 3, 4 } };
check(len(&(s)) == 2);
check(s[0].x == 1);
check(s[0].y == 2);
check(s[1].x == 3);
check(s[1].y == 4);
add(&(s), Point { 5, 6 });
check(s[2].x == 5);
check(s[2].y == 6);
}
{
auto s = Seq<Point> { Point { .x = 1, .y = 2 }, Point { .x = 3, .y = 4 } };
check(len(&(s)) == 2);
}
{
auto s = Seq<Seq<int>> { Seq<int> { 1 }, Seq<int> {}, Seq<int> { 3, 4 } };
check(len(&(s)) == 3);
check(gx::len(s[0]) == 1);
check(s[0][0] == 1);
check(len(&(s[1])) == 0);
check(len(&(s[2])) == 2);
check(s[2][0] == 3);
check(s[2][1] == 4);
}
{
auto s = Seq<SingleIncr> { SingleIncr { 1 }, SingleIncr { 2 }, SingleIncr { 3 } };
incrSeq<SingleIncr, SingleIncr *>(&s);
check(s[0].val == 2);
check(s[1].val == 3);
check(s[2].val == 4);
}
{
auto s = Seq<DoubleIncr> { DoubleIncr { 1 }, DoubleIncr { 2 }, DoubleIncr { 3 } };
incrSeq<DoubleIncr, DoubleIncr *>(&s);
check(s[0].val == 3);
check(s[1].val == 4);
check(s[2].val == 5);
}
}
void setGlobalXToFortyTwo() {
globalX = 42;
}
void checkGlobalXIsFortyTwo() {
check(globalX == 42);
}
int three() {
return 3;
}
bool isGlobalSliceEmpty() {
return gx::len(globalSlice) == 0;
}
int apply(int val, auto &&fn) {
return fn(val);
}
void testGlobalVariables() {
{
check(globalX == 23);
check(globalY == 20);
check(globalZ == 14);
setGlobalXToFortyTwo();
checkGlobalXIsFortyTwo();
check(initialGlobalX == 23);
}
{
check(isGlobalSliceEmpty());
globalSlice = gx::append(globalSlice, 1);
globalSlice = gx::append(globalSlice, 2);
check(gx::len(globalSlice) == 2);
check(globalSlice[0] == 1);
check(globalSlice[1] == 2);
check(!isGlobalSliceEmpty());
}
{
check(globalApplied == 6);
}
{
check(ZeroEnum == 0);
check(OneEnum == 1);
check(TwoEnum == 2);
}
}
void testImports() {
{
auto f = Foo {};
check(Val(&(f)) == 0);
}
{
auto f = NewFoo(42);
check(Val(&(f)) == 42);
}
{
auto b = Bar { .X = 2, .Y = 3 };
check(b.X == 2);
check(b.Y == 3);
}
}
void testExterns() {
{
check(rect::NUM_VERTICES == 4);
auto r = rect::Rect { .x = 100, .y = 100, .width = 20, .height = 30 };
check(r.x == 100);
check(r.y == 100);
check(r.width == 20);
check(r.height == 30);
check(rect::area(r) == 600);
check(rect::area(r) == 600);
}
{
check(person::Population == 0);
auto p = person::NewPerson(20, 100);
check(person::Population == 1);
check(person::GetAge(p) == 20);
check(person::GetHealth(p) == 100);
person::Grow(&(p));
check(person::GetAge(p) == 21);
check(p.cppValue == 42);
check(person::GetAgeAdder(&(p))(1) == 22);
}
}
void testConversions() {
{
auto f = float(2.2f);
auto i = int(f);
check(i == 2);
auto d = 2.2f;
check(f - float(d) == 0);
}
{
auto slice = gx::Slice<int> { 1, 2 };
auto seq = Seq<int>(slice);
add(&(seq), 3);
check(len(&(seq)) == 3);
check(seq[0] == 1);
check(seq[1] == 2);
check(seq[2] == 3);
}
}
void testMeta() {
auto n = Nums { 1, 2, 3, 4 };
check(sumFields(n) == 14);
}
void testDefaults() {
auto h = HasDefaults {};
check(h.foo == 42);
check(h.bar == 6.4f);
check(h.point.x == 1);
check(h.point.y == 2);
}
void testStrings() {
{
gx::String s0 = "";
check(gx::len(s0) == 0);
check(std::strcmp(s0, "") == 0);
gx::String s1 = "foo";
check(gx::len(s1) == 3);
check(s1[0] == 'f');
check(s1[1] == 'o');
check(s1[2] == 'o');
check(std::strcmp(s1, "foo") == 0);
gx::String s2 = "foo";
check(std::strcmp(s1, s2) == 0);
check(std::strcmp(s1, "nope") != 0);
check(std::strcmp(s1, "foo") == 0);
check(s1 == s2);
check(s1 != "nope");
check(s1 == gx::String("foo"));
check(s1 != gx::String("fao"));
gx::String s3 = s2;
check(std::strcmp(s1, s3) == 0);
auto sum = 0;
for (auto i = -1; auto &c : s3) {
++i;
sum += i;
if (i == 0) {
check(c == 'f');
}
if (i == 1) {
check(c == 'o');
}
if (i == 2) {
check(c == 'o');
}
}
check(sum == 3);
}
{
auto h0 = HasString {};
check(gx::len(h0.s) == 0);
check(std::strcmp(h0.s, "") == 0);
auto h1 = HasString { "foo" };
check(gx::len(h1.s) == 3);
check(h1.s[0] == 'f');
check(h1.s[1] == 'o');
check(h1.s[2] == 'o');
check(std::strcmp(h1.s, "foo") == 0);
auto h2 = HasString { "foo" };
check(std::strcmp(h1.s, h2.s) == 0);
check(std::strcmp(h1.s, HasString { "nope" }.s) != 0);
check(std::strcmp(h1.s, HasString { "foo" }.s) == 0);
auto h3 = h2;
check(std::strcmp(h1.s, h3.s) == 0);
}
}
int main() {
testFib();
testUnary();
testVariables();
testIncDec();
testIf();
testFor();
testPointer();
testStruct();
testMethod();
testGenerics();
testLambdas();
testArrays();
testSlices();
testSeqs();
testGlobalVariables();
testImports();
testExterns();
testConversions();
testMeta();
testDefaults();
testStrings();
}
void check(bool val) {
if (val) {
gx::println("ok");
} else {
gx::println("not ok");
}
}
int Val(Foo *f) {
return gx::deref(f).val;
}
Foo NewFoo(int val) {
return Foo { val };
}
//gx:include <string.h>
//gx:include "rect.hh"
//gx:include "sum_fields.hh"
package main
import (
"github.com/nikki93/gx/example/foo"
"github.com/nikki93/gx/example/person"
)
//
// Basics
//
func fib(n int) int {
if n <= 1 {
return n
} else {
return fib(n-1) + fib(n-2)
}
}
func testFib() {
check(fib(6) == 8)
}
func testUnary() {
check(-(3) == -3)
check(+(3) == 3)
}
func testVariables() {
x := 3
y := 4
check(x == 3)
check(y == 4)
y = y + 2
x = x + 1
check(y == 6)
check(x == 4)
y += 2
x += 1
check(y == 8)
check(x == 5)
}
func testIncDec() {
x := 0
x++
check(x == 1)
x--
check(x == 0)
}
func testIf() {
x := 0
if cond := false; cond {
x = 2
}
check(x == 0)
}
func testFor() {
{
sum := 0
for i := 0; i < 5; i++ {
sum += i
}
check(sum == 10)
}
{
sum := 0
i := 0
for i < 5 {
sum += i
i++
}
check(sum == 10)
}
{
sum := 0
i := 0
for {
if i >= 5 {
break
}
sum += i
i++
}
check(sum == 10)
}
}
//
// Pointers
//
func setToFortyTwo(ptr *int) {
*ptr = 42
}
func testPointer() {
val := 42
check(val == 42)
ptr := &val
*ptr = 14
check(val == 14)
setToFortyTwo(ptr)
check(val == 42)
}
//
// Structs
//
type Outer struct {
x int
y int
inner Inner
}
type Inner struct {
z int
}
func outerSum(o Outer) int {
return o.x + o.y + o.inner.z
}
func setXToFortyTwo(o *Outer) {
o.x = 42
}
type PtrPtr struct {
pp **int // Should be formatted as `int **pp;` in C++
}
func testStruct() {
{
s := Outer{}
check(s.x == 0)
check(s.y == 0)
check(s.inner.z == 0)
{
p := &s
p.x = 2
check(p.x == 2)
check(s.x == 2)
s.y = 4
check(p.y == 4)
}
check(outerSum(s) == 6)
setXToFortyTwo(&s)
check(s.x == 42)
}
{
s := Outer{2, 3, Inner{4}}
check(s.x == 2)
check(s.y == 3)
check(s.inner.z == 4)
s.x += 1
s.y += 1
s.inner.z += 1
check(s.x == 3)
check(s.y == 4)
check(s.inner.z == 5)
}
{
s := Outer{x: 2, y: 3, inner: Inner{z: 4}}
check(s.x == 2)
check(s.y == 3)
check(s.inner.z == 4)
}
{
s := Outer{
x: 2,
y: 3,
inner: Inner{
z: 4,
},
}
check(s.x == 2)
check(s.y == 3)
check(s.inner.z == 4)
}
{
// Out-of-order elements in struct literal no longer allowed
//s := Outer{
// inner: Inner{
// z: 4,
// },
// y: 3,
// x: 2,
//}
}
{
i := 42
p := &i
pp := &p
d := PtrPtr{pp}
**d.pp = 14
check(d.pp != nil)
check(i == 14)
}
{
p := PtrPtr{}
check(p.pp == nil)
}
}
//
// Methods
//
type Point struct {
x, y float32
}
func (p Point) sum() float32 {
return p.x + p.y
}
func (p *Point) setZero() {
p.x = 0
p.y = 0
}
func testMethod() {
p := Point{2, 3}
check(p.sum() == 5)
ptr := &p
check(ptr.sum() == 5) // Pointer as value receiver
p.setZero() // Addressable value as pointer receiver
check(p.x == 0)
check(p.y == 0)
}
//
// Generics
//
type Numeric interface {
int | float64
}
func add[T Numeric](a, b T) T {
return a + b
}
type Holder[T any] struct {
Item T
}
func incrHolder[T Numeric](h *Holder[T]) {
h.Item += 1
}
func (h Holder[T]) get() T {
return h.Item
}
func (h *Holder[T]) set(Item T) {
h.Item = Item
}
func testGenerics() {
{
check(add(1, 2) == 3)
check(add(1.2, 2.0) == 3.2)
check(add[float64](1.2, 2.0) == 3.2)
}
{
i := Holder[int]{42}
check(i.Item == 42)
incrHolder(&i)
check(i.Item == 43)
f := Holder[float64]{42}
check(f.Item == 42)
check(add(f.Item, 20) == 62)
incrHolder(&f)
check(f.Item == 43)
p := Holder[Point]{Point{1, 2}}
check(p.Item.x == 1)
check(p.Item.y == 2)
p.Item.setZero()
check(p.Item.x == 0)
check(p.Item.y == 0)
p.set(Point{3, 2})
check(p.Item.x == 3)
check(p.Item.y == 2)
check(p.get().x == 3)
check(p.get().y == 2)
}
}
//
// Lambdas
//
func iterateOneToTen(f func(int)) {
for i := 1; i <= 10; i++ {
f(i)
}
}
func testLambdas() {
{
val := 42
check(val == 42)
foo := func(newVal int) {
val = newVal
}
foo(14)
check(val == 14)
val2 := func() int {
return val
}()
check(val2 == val)
}
{
sum := 0
iterateOneToTen(func(i int) {
sum += i
})
check(sum == 55)
}
}
//
// Arrays
//
func setSecondElementToThree(arr *[4]int) {
arr[1] = 3
}
type HasArray struct {
arr [4]int
}
func testArrays() {
{
arr := [4]int{1, 2, 3, 4}
check(arr[2] == 3)
sum := 0
for i := 0; i < len(arr); i++ {
sum += arr[i]
}
check(sum == 10)
check(arr[1] == 2)
setSecondElementToThree(&arr)
check(arr[1] == 3)
}
{
stuff := [...]int{1, 2, 3}
check(len(stuff) == 3)
sum := 0
for i, elem := range stuff {
check(i+1 == elem)
sum += elem
}
check(sum == 6)
// Other cases of for-range are checked in `testSlices`
}
{
arr := [...][2]int{{1, 2}, {3, 4}}
check(len(arr) == 2)
check(arr[0][0] == 1)
check(arr[0][1] == 2)
check(arr[1][0] == 3)
check(arr[1][1] == 4)
}
{
h := HasArray{}
check(len(h.arr) == 4)
check(h.arr[0] == 0)
check(h.arr[1] == 0)
check(h.arr[2] == 0)
check(h.arr[3] == 0)
}
{
h := HasArray{[4]int{1, 2, 3, 4}}
check(len(h.arr) == 4)
check(h.arr[2] == 3)
}
}
//
// Slices
//
func appendFortyTwo(s *[]int) {
*s = append(*s, 42)
}
func testSlices() {
{
s := []int{}
check(len(s) == 0)
s = append(s, 1)
s = append(s, 2)
check(len(s) == 2)
check(s[0] == 1)
check(s[1] == 2)
appendFortyTwo(&s)
check(len(s) == 3)
check(s[2] == 42)
}
{
s := [][]int{{1}, {}, {3, 4}}
check(len(s) == 3)
check(len(s[0]) == 1)
check(s[0][0] == 1)
check(len(s[1]) == 0)
check(len(s[2]) == 2)
check(s[2][0] == 3)
check(s[2][1] == 4)
}
{
stuff := []int{1, 2}
stuff = append(stuff, 3)
check(len(stuff) == 3)
{
sum := 0
for i, elem := range stuff {
check(i+1 == elem)
sum += elem
}
check(sum == 6)
}
{
sum := 0
for i := range stuff {
sum += i
}
check(sum == 3)
}
{
sum := 0
for _, elem := range stuff {
sum += elem
}
check(sum == 6)
}
{
count := 0
for range stuff {
count += 1
}
check(count == 3)
}
{
stuff = []int{}
count := 0
for range stuff {
count += 1
}
check(count == 0)
}
}
}
//
// Seq (generic slice with own methods)
//
type Seq[T any] []T
func (s *Seq[T]) len() int {
return len(*s)
}
func (s *Seq[T]) add(val T) {
*s = append(*s, val)
}
type Increr[T any] interface {
*T
incr()
}
func incrSeq[T any, PT Increr[T]](s *Seq[T]) {
for i := range *s {
PT(&(*s)[i]).incr()
}
}
type SingleIncr struct {
val int
}
func (s *SingleIncr) incr() {
s.val += 1
}
type DoubleIncr struct {
val int
}
func (s *DoubleIncr) incr() {
s.val += 2
}
func testSeqs() {
{
s := Seq[int]{}
check(s.len() == 0)
s.add(1)
s.add(2)
check(s.len() == 2)
check(s[0] == 1)
check(s[1] == 2)
}
{
s := Seq[int]{1, 2, 3}
check(s.len() == 3)
sum := 0
for i, elem := range s {
check(i+1 == elem)
sum += elem
}
check(sum == 6)
}
{
s := Seq[Point]{{1, 2}, {3, 4}}
check(s.len() == 2)
check(s[0].x == 1)
check(s[0].y == 2)
check(s[1].x == 3)
check(s[1].y == 4)
s.add(Point{5, 6})
check(s[2].x == 5)
check(s[2].y == 6)
}
{
s := Seq[Point]{{x: 1, y: 2}, {x: 3, y: 4}}
check(s.len() == 2)
}
{
s := Seq[Seq[int]]{{1}, {}, {3, 4}}
check(s.len() == 3)
check(len(s[0]) == 1)
check(s[0][0] == 1)
check(s[1].len() == 0)
check(s[2].len() == 2)
check(s[2][0] == 3)
check(s[2][1] == 4)
}
{
s := Seq[SingleIncr]{{1}, {2}, {3}}
incrSeq(&s)
check(s[0].val == 2)
check(s[1].val == 3)
check(s[2].val == 4)
}
{
s := Seq[DoubleIncr]{{1}, {2}, {3}}
incrSeq(&s)
check(s[0].val == 3)
check(s[1].val == 4)
check(s[2].val == 5)
}
}
//
// Global variables
//
var globalY = globalX - three()
var globalX, globalZ = initialGlobalX, 14
var globalSlice []int
const initialGlobalX = 23
func setGlobalXToFortyTwo() {
globalX = 42
}
func checkGlobalXIsFortyTwo() {
check(globalX == 42)
}
func three() int {
return 3
}
func isGlobalSliceEmpty() bool {
return len(globalSlice) == 0
}
func apply(val int, fn func(int) int) int {
return fn(val)
}
var globalApplied = apply(3, func(i int) int { return 2 * i })
type Enum int
const (
ZeroEnum Enum = 0
OneEnum = 1
TwoEnum = 2
)
func testGlobalVariables() {
{
check(globalX == 23)
check(globalY == 20)
check(globalZ == 14)
setGlobalXToFortyTwo()
checkGlobalXIsFortyTwo()
check(initialGlobalX == 23)
}
{
check(isGlobalSliceEmpty())
globalSlice = append(globalSlice, 1)
globalSlice = append(globalSlice, 2)
check(len(globalSlice) == 2)
check(globalSlice[0] == 1)
check(globalSlice[1] == 2)
check(!isGlobalSliceEmpty())
}
{
check(globalApplied == 6)
}
{
check(ZeroEnum == 0)
check(OneEnum == 1)
check(TwoEnum == 2)
}
}
//
// Imports
//
func testImports() {
{
f := foo.Foo{}
check(f.Val() == 0)
}
{
f := foo.NewFoo(42)
check(f.Val() == 42)
}
{
b := foo.Bar{X: 2, Y: 3}
check(b.X == 2)
check(b.Y == 3)
}
}
//
// Externs
//
//gx:extern rect::NUM_VERTICES
const RectNumVertices = 0 // Ensure use of actual C++ constant value
//gx:extern rect::Rect
type Rect struct {
X, Y float32
Width, Height float32
}
//gx:extern rect::area
func area(r Rect) float32
//gx:extern rect::area
func (r Rect) area() float32
func testExterns() {
{
check(RectNumVertices == 4)
r := Rect{X: 100, Y: 100, Width: 20, Height: 30}
check(r.X == 100)
check(r.Y == 100)
check(r.Width == 20)
check(r.Height == 30)
check(area(r) == 600)
check(r.area() == 600)
}
{
check(person.Population == 0)
p := person.NewPerson(20, 100)
check(person.Population == 1)
check(p.Age() == 20)
check(p.Health() == 100)
p.Grow()
check(p.Age() == 21)
check(p.GXValue == 42)
check(p.GetAgeAdder()(1) == 22)
}
}
//
// Conversions
//
func testConversions() {
{
f := float32(2.2)
i := int(f)
check(i == 2)
d := 2.2
check(f-float32(d) == 0)
}
{
slice := []int{1, 2}
seq := Seq[int](slice)
seq.add(3)
check(seq.len() == 3)
check(seq[0] == 1)
check(seq[1] == 2)
check(seq[2] == 3)
}
}
//
// Meta
//
type Nums struct {
A, B, C int
D int `attribs:"twice"`
}
//gx:extern sumFields
func sumFields(val interface{}) int
func testMeta() {
n := Nums{1, 2, 3, 4}
check(sumFields(n) == 14)
}
//
// Defaults
//
type HasDefaults struct {
foo int `default:"42"`
bar float32 `default:"6.4"`
point Point `default:"{ 1, 2 }"`
}
func testDefaults() {
h := HasDefaults{}
check(h.foo == 42)
check(h.bar == 6.4)
check(h.point.x == 1)
check(h.point.y == 2)
}
//
// Strings
//
//gx:extern std::strcmp
func strcmp(a, b string) int
type HasString struct {
s string
}
func testStrings() {
{
s0 := ""
check(len(s0) == 0)
check(strcmp(s0, "") == 0)
s1 := "foo"
check(len(s1) == 3)
check(s1[0] == 'f')
check(s1[1] == 'o')
check(s1[2] == 'o')
check(strcmp(s1, "foo") == 0)
s2 := "foo"
check(strcmp(s1, s2) == 0)
check(strcmp(s1, "nope") != 0)
check(strcmp(s1, "foo") == 0)
check(s1 == s2)
check(s1 != "nope")
check(s1 == string("foo"))
check(s1 != string("fao"))
s3 := s2
check(strcmp(s1, s3) == 0)
sum := 0
for i, c := range s3 {
sum += i
if i == 0 {
check(c == 'f')
}
if i == 1 {
check(c == 'o')
}
if i == 2 {
check(c == 'o')
}
}
check(sum == 3)
}
{
h0 := HasString{}
check(len(h0.s) == 0)
check(strcmp(h0.s, "") == 0)
h1 := HasString{"foo"}
check(len(h1.s) == 3)
check(h1.s[0] == 'f')
check(h1.s[1] == 'o')
check(h1.s[2] == 'o')
check(strcmp(h1.s, "foo") == 0)
h2 := HasString{"foo"}
check(strcmp(h1.s, h2.s) == 0)
check(strcmp(h1.s, HasString{"nope"}.s) != 0)
check(strcmp(h1.s, HasString{"foo"}.s) == 0)
h3 := h2
check(strcmp(h1.s, h3.s) == 0)
}
}
//
// Main
//
func main() {
testFib()
testUnary()
testVariables()
testIncDec()
testIf()
testFor()
testPointer()
testStruct()
testMethod()
testGenerics()
testLambdas()
testArrays()
testSlices()
testSeqs()
testGlobalVariables()
testImports()
testExterns()
testConversions()
testMeta()
testDefaults()
testStrings()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment