Examples
Each example is taken from the test suite for Tolc and, given that you use the latest version, you can expect them all to work.
Each C++ library named MyLib exports their functions and objects with a prefix of MyLib. In every test the library name is simply m for brevity. The examples that follow contains a bit of C++ code, and the respective Objective-C and Swift code using it. Each Objective-C example is wrapped in the following boilerplate that is removed to make the examples more readable:
#include <m.h>
int main() {
  @autoreleasepool {
    assert([m sayHello] == "Hello");
  }
}
And the same for Swift:
import m_swift
assert(m.sayHello() == "Hello")
Classes
class WithConstructor {
public:
  explicit WithConstructor() : m_v(10) {}
  explicit WithConstructor(int v) : m_v(v) {}
  int getV() { return m_v; }
private:
  int m_v;
};
class WithFunction {
public:
  int add(int i, int j) {
    return i + j;
  }
};
class WithStatic {
public:
  static double getPi() {
    return 3.14;
  }
  static int const answer = 42;
};
class WithMember {
public:
  explicit WithMember() : i(10), phi(1.618) {}
  int i;
  double const phi;
};
// Constructors are overloaded with their argument types
mWithConstructor* ten = [[mWithConstructor alloc] init];
assert([ten getV] == 10);
mWithConstructor* five = [[mWithConstructor alloc] initWithInt:5];
assert([five getV] == 5);
// Member functions are available after construction
mWithFunction* withFunction = [[mWithFunction alloc] init];
assert([withFunction add: 2 j: 5] == 7);
// Static functions can be called
// without instantiating the class
assert([mWithStatic getPi] == 3.14);
// You can access static variables
// without instantiating the class
assert([mWithStatic answer] == 42);
// Member variables
mWithMember* member = [[mWithMember alloc] init];
assert(member.i == 10);
// i is not marked const
member.i = 5;
assert(member.i == 5);
// phi is marked const
// Cannot be assigned
assert(member.phi == 1.618);
Enums
enum Unscoped {
    Under,
    Uboat,
};
enum class Scoped {
    Sacred,
    Snail,
};
class EnumTest {
public:
    explicit EnumTest(Scoped _s) : s(_s) {};
    Unscoped f(Unscoped u) {
        return u;
    }
    Scoped s;
};
namespace NS {
    // Documentation describing the enum
    enum class Deep {
        Double,
        Down,
    };
}
// C++11 enums work
mEnumTest* enumTest = [[mEnumTest alloc] initWithScoped:mScopedSnail];
mScoped snail = mScopedSnail;
assert(enumTest.s == snail);
// Aswell as legacy enums
mUnscoped uboat = mUnscopedUboat;
assert([enumTest f:uboat] == uboat);
// Enums under namespaces are available
// under the corresponding submodule
/* deep = m.NS.Deep.Down */
/* assert(deep != m.NS.Deep.Double) */
// Documentation carries over from C++
// self.assertIn("Documentation describing the enum", m.NS.Deep.__doc__)
Functions
#include <filesystem>
#include <string>
int meaningOfLife() {
    return 42;
}
std::string sayHello(std::string const& name) {
    return "Hello " + name;
}
std::filesystem::path getPath() {
    return std::filesystem::path("/path/to/stuff.hpp");
}
namespace Inner {
    double pi() {
        return 3.14;
    }
}
// Global functions gets added to
// a purely static class with
// the name of the library
assert([m meaningOfLife] == 42);
// Strings can be used
assert([[m sayHello:@"Tolc"] isEqualToString:@"Hello Tolc"]);
// Aswell as filesystem paths
assert([[m getPath] isEqualToString:@"/path/to/stuff.hpp"]);
// Functions within namespaces
// are available with the
// namespaces names merged
assert([mInner pi] == 3.14);
Global Variables
#include <string>
#include <string_view>
static int i = 0;
namespace Nested {
    int life = 42;
    std::string s = "Hello World";
    constexpr std::string_view constant = "A constant";
}
// Starts at 0 and can be changed
assert(m.i == 0);
m.i = 5;
assert(m.i == 5);
// Nested with the same name
assert(mNested.life == 42);
// Strings also work
assert([mNested.s isEqualToString:@"Hello World"]);
// And string_view
assert([mNested.constant isEqualToString:@"A constant"]);
Member Variables
#include <string>
class SimpleMember {
public:
    explicit SimpleMember() : myString("Hello") {}
    std::string myString;
};
class ConstMember {
public:
    const int i = 42;
};
class PrivateMember {
public:
    explicit PrivateMember(std::string s) : myString(s) {}
private:
    std::string myString;
};
namespace MyLib {
    class Nested {
    public:
        double d = 4.3;
    };
}
// Mutable member variables can be changed
mSimpleMember* simpleMember = [[mSimpleMember alloc] init];
assert([simpleMember.myString isEqualToString:@"Hello"]);
simpleMember.myString = @"Changed now!";
assert([simpleMember.myString isEqualToString:@"Changed now!"]);
mConstMember* constMember = [[mConstMember alloc] init];
assert(constMember.i == 42);
mMyLibNested* nested = [[mMyLibNested alloc] init];
assert(nested.d == 4.3);
Namespaces
#include <string>
/*
* MyLib contains a bunch of MyLib functions
*/
namespace MyLib {
int complexFunction() {
    return 5;
}
    namespace We {
        namespace Are {
            namespace Going {
                namespace Pretty {
                    namespace Deep {
                        std::string meaningOfLife() {
                            return "42";
                        }
                    }
                }
            }
        }
    }
}
// Namespaces corresponds to classes
// with {library name} + join(namespaces)
// where functions are static class functions
assert([mMyLib complexFunction] == 5);
// You can nest namespaces arbitrarily deep
NSString* lifeProTips = [mMyLibWeAreGoingPrettyDeep meaningOfLife];
assert([lifeProTips isEqualToString:@"42"]);
Overloaded Functions
#include <string>
// Overloaded free functions
std::string sayHello() {
    return "Hello!";
}
std::string sayHello(std::string to) {
    return std::string("Hello ") + to;
}
std::string sayHello(size_t times) {
    std::string greeting = "";
    for (size_t i = 0; i < times; ++i) {
        greeting += "Hello!";
    }
    return greeting;
}
class Overload {
public:
    // Overloaded constructor
    Overload() : m_s() {};
    Overload(std::string s) : m_s(s) {};
    // Overloaded class functions
    std::string getStuff() { return "Stuff"; }
    std::string getStuff(std::string customStuff) { return customStuff; }
private:
    std::string m_s;
};
// Overloaded functions work the same as in C++
// Free function overload
assert([[m sayHello] isEqualToString:@"Hello!"]);
assert([[m sayHelloString:@"Tolc"] isEqualToString:@"Hello Tolc"]);
assert([[m sayHelloUnsignedLongInt:2] isEqualToString:@"Hello!Hello!"]);
// Class constructor overload
mOverload* overload = [[mOverload alloc] init];
mOverload* overloadWithString = [[mOverload alloc] initWithString:@"Overloaded!"];
// Class function overload
assert([[overload getStuff] isEqualToString:@"Stuff"]);
assert([[overload getStuffString:@"Other"] isEqualToString:@"Other"]);
Passing classes between languages
#include <string>
class MyClass {
public:
    explicit MyClass(std::string s) : m_s(s) {}
    std::string* getS() { return &m_s; }
private:
    std::string m_s;
};
MyClass buildMyClass(std::string const& s) {
    return MyClass(s);
}
class Owner {
public:
    explicit Owner(MyClass m) : m_myClass(m) {};
    MyClass getMyClass() const { return m_myClass; }
private:
    MyClass m_myClass;
};
struct Point2d {
    int x;
    int y;
};
Point2d getMiddle(std::pair<Point2d, Point2d> p) {
    return {(p.first.x + p.second.x) / 2, (p.first.y + p.second.y) / 2};
}
NSString* phrase = @"Hello from Objective-C";
mMyClass* myClass = [m buildMyClass:phrase];
assert([[myClass getS] isEqualToString:phrase]);
// Passing Objective-C classes to C++ classes
mOwner* owner = [[mOwner alloc] initWithMyClass:myClass];
assert([[[owner getMyClass] getS] isEqualToString:phrase]);
// Container of user defined classes
mPoint2d* a = [[mPoint2d alloc] init];
a.x = 1;
a.y = 0;
mPoint2d* b = [[mPoint2d alloc] init];
b.x = 3;
b.y = 0;
NSArray* points = [NSArray arrayWithObjects:a, b, nil];
mPoint2d* middle = [m getMiddle:points];
assert(middle.x == 2);
assert(middle.y == 0);
Smart Pointers
#include <memory>
struct Data {
  int i = 5;
};
struct SharedData {
  int i = 10;
};
std::unique_ptr<Data> createData() {
  return std::make_unique<Data>();
}
// This moves the data,
// destroying it at the end
// Same as C++
int consumeData(std::unique_ptr<Data> data) {
  return data->i + 20;
}
std::shared_ptr<SharedData> createSharedData() {
  return std::make_shared<SharedData>();
}
// Does not move the data
// The pointer is valid after the function call
int consumeSharedData(std::shared_ptr<SharedData> data) {
  return data->i + 20;
}
// std::unique_ptr acts as a normal value
mData* data = [m createData];
assert(data.i == 5);
// This moves the data,
// destroying it at the end
// Same as C++
assert([m consumeData:data] == 25);
// Any access now results
// in undefined behaviour
// (possibly a crash)
// NSLog(@"%i", data.i);
// std::shared_ptr acts as a normal value
// But all mSharedData have their internal
// classes handled by a std::shared_ptr
mSharedData* sharedData = [m createSharedData];
assert(sharedData.i == 10);
// This copies the smart pointer,
// incrementing its counter.
// Valid to use sharedData after this call.
assert([m consumeSharedData:sharedData] == 30);
// No crash
NSLog(@"%i", sharedData.i);
Templates
#include <array>
#include <map>
#include <string>
#include <vector>
template <typename T>
T getSomething(T something) {
  return something;
}
template std::string getSomething(std::string something);
template int getSomething(int);
template std::vector<std::string> getSomething(std::vector<std::string>);
template <typename T>
class MyClass {
public:
T myFun(T type) {
    return type;
}
};
MyClass<char> getMyClass(MyClass<char> c) {
    return c;
}
template class MyClass<int>;
template class MyClass<std::array<int, 3>>;
// getSomething<std::string>
NSString* hi = [m getSomethingString:@"Hi"];
assert([hi isEqualToString:@"Hi"]);
// getSomething<int>
int five = [m getSomethingInt:5];
assert(five == 5);
// getSomething<std::vector<std::string>>
NSArray* v = [m getSomethingVectorString:@[@"Hi"]];
assert([v count] == 1);
assert([[v objectAtIndex:0] isEqualToString:@"Hi"]);
// MyClass<char>
mMyClassChar* myClassChar = [[mMyClassChar alloc] init];;
assert([myClassChar myFun:25] == 25);;
// Still the same after passing through a function
mMyClassChar* passedThrough = [m getMyClass:myClassChar];;
assert([passedThrough myFun:25] == 25);;
// MyClass<int>
mMyClassInt* myClassInt = [[mMyClassInt alloc] init];
assert([myClassInt myFun:25] == 25);
// MyClass<std::array<int, 3>>
mMyClassArrayInt3* myClassArray = [[mMyClassArrayInt3 alloc] init];
NSArray* arr = [myClassArray myFun:@[@(0), @(1), @(2)]];
assert([arr count] == 3);
assert([[arr objectAtIndex:0] intValue] == 0);
assert([[arr objectAtIndex:1] intValue] == 1);
assert([[arr objectAtIndex:2] intValue] == 2);
std::array
#include <algorithm>
#include <array>
std::array<int, 3> const f() {
  return {0, 1, 2};
}
bool allOf(std::array<bool, 3> const& conditions) {
  return std::all_of(
      conditions.begin(), conditions.end(),
      [](auto c) { return c; });
}
double sum(std::array<double, 3> const& numbers) {
  double sum = 0;
  for (double number : numbers) {
    sum += number;
  }
  return sum;
}
// std::array corresponds to NSArray
NSArray* v = [m f];
assert([v count] == 3);
// The array contains {0, 1, 2}
assert([[v objectAtIndex:0] intValue] == 0);
assert([[v objectAtIndex:1] intValue] == 1);
assert([[v objectAtIndex:2] intValue] == 2);
// Sending NSArray into function works as well
NSArray* conditions = @[@(YES), @(YES), @(NO)];
assert([m allOf:conditions] == NO);
NSArray<NSNumber*>* toSum = @[@(1.1), @(2.2), @(3.3)];
assert([m sum:toSum] == 6.6);
// Error handling
@try {
  // Array with the wrong size
  NSArray<NSNumber*>* toSum = @[@(1.1), @(2.2)];
  // Expected size == 3
  [m sum:toSum];
  // Should throw exception before
  assert(NO);
} @catch(NSException* error) {
  assert([[error name] isEqualToString:@"TypeException"]);
  NSString* reason =
    @"The size of the array does not match the expected fixed size. Expected: 3, Got: 2.";
  assert([[error reason] isEqualToString:reason]);
}
std::deque
#include <string>
#include <deque>
std::deque<std::string>
surround(std::deque<std::string> d,
         std::string const& message) {
  d.push_front(message);
  d.push_back(message);
  return d;
}
// std::deque corresponds to NSArray
NSArray* myDeque = @[@"middle"];
NSArray* surroundedDeque =
  [m surround:myDeque message:@"surrounded"];
assert([surroundedDeque count] == 3);
assert([[surroundedDeque objectAtIndex:0]
  isEqualToString:@"surrounded"]);
assert([[surroundedDeque objectAtIndex:1]
  isEqualToString:@"middle"]);
assert([[surroundedDeque objectAtIndex:2]
  isEqualToString:@"surrounded"]);
std::filesystem::path
#include <filesystem>
#include <vector>
std::filesystem::path
takingPath(std::filesystem::path const& p) {
    return p;
}
std::filesystem::path
parent(std::filesystem::path const& p) {
    return p.parent_path();
}
std::filesystem::path
joinPaths(std::vector<std::filesystem::path> arrayToSum) {
    std::filesystem::path sum;
    for (auto f : arrayToSum) {
        sum /= f;
    }
    return sum;
}
// std::filesystem::path corresponds to NSString
NSString* path = @"Hello/my/name/is/Tolc";
// Passing through a function
NSString* result = [m takingPath:path];
assert([result isEqualToString:path]);
NSString* parent = [m parent:path];
assert([parent isEqualToString:@"Hello/my/name/is"]);
NSArray* paths = @[@"to", @"the", @"heart"];
NSString* joined = [m joinPaths:paths];
assert([joined isEqualToString:@"to/the/heart"]);
std::list
#include <string>
#include <list>
std::list<std::string> getList() {
  return {"Linked", "list", "fun"};
}
// std::list corresponds to NSArray
NSArray* words = [m getList];
assert([words count] == 3);
assert([[words objectAtIndex:0] isEqualToString:@"Linked"]);
assert([[words objectAtIndex:1] isEqualToString:@"list"]);
assert([[words objectAtIndex:2] isEqualToString:@"fun"]);
std::map
#include <map>
#include <string>
#include <vector>
std::map<std::string, int> getThings() {
  return {{"Greetings", 5}};
}
std::map<std::string, std::vector<double>> getCities() {
  return {
  {"Stockholm",
    {59.33, 18.06}},
  {"San Francisco",
    {37.77, -122.43}}
  };
}
// std::map translates to a NSDictionary
NSDictionary* dict = [m getThings];
assert([dict count] == 1);
NSNumber* n = [dict objectForKey:@"Greetings"];
assert(n != nil);
assert([n intValue] == 5);
// Nested containers work as well
NSDictionary* cities = [m getCities];
assert([cities count] == 2);
NSArray* stockholm = [cities objectForKey:@"Stockholm"];
assert(stockholm != nil);
assert([stockholm count] == 2);
assert([[stockholm objectAtIndex:0] doubleValue] == 59.33);
assert([[stockholm objectAtIndex:1] doubleValue] == 18.06);
NSArray* sanFrancisco = [cities objectForKey:@"San Francisco"];
assert(sanFrancisco != nil);
assert([sanFrancisco count] == 2);
assert([[sanFrancisco objectAtIndex:0] doubleValue] == 37.77);
assert([[sanFrancisco objectAtIndex:1] doubleValue] == -122.43);
std::optional
#include <optional>
#include <string>
std::string
answer(std::optional<std::string> const& question) {
  if (question) {
    return "Please be more specific.";
  }
  return "That's no question!";
}
// std::optional is either the value or nil
NSString* answer = [m answer:@"How do I take over the world?"];
assert([answer isEqualToString:@"Please be more specific."]);
// nil is the equivalent of std::nullopt on the C++ side
NSString* noAnswer = [m answer:nil];
assert([noAnswer isEqualToString:@"That's no question!"]);
std::pair
#include <string>
class Greeter {
public:
  explicit Greeter(std::pair<std::string, int> greetings)
    : m_greetings(greetings) {}
  std::pair<std::string, int> getGreetings() {
    return m_greetings;
  }
  std::string joinGreetings() {
    std::string joined;
    for (int i = 0; i < m_greetings.second; ++i) {
      joined += m_greetings.first;
    }
    return joined;
  }
private:
  std::pair<std::string, int> m_greetings;
};
// std::pair corresponds to a NSArray
// with two values
NSArray* greetings = [NSArray
  arrayWithObjects:@"Hey ", @(3), nil];
assert([greetings count] == 2);
// Sending a pair to a function
mGreeter* g = [[mGreeter alloc]
  initWithPairStringInt:greetings];
// Joining the greetings 3 times
NSString* joined = [g joinGreetings];
assert([joined isEqualToString:@"Hey Hey Hey "]);
// Error handling
@try {
  // Sending an array with size != 2
  NSArray* tooManyArgs =
    [greetings arrayByAddingObject:@"Oh no"];
  mGreeter* boom = [[mGreeter alloc]
    initWithPairStringInt:tooManyArgs];
  // Should throw exception before
  assert(NO);
} @catch(NSException* error) {
  assert([[error name] isEqualToString:@"TypeException"]);
  NSString* reason =
    @"The array passed does not match the number of types in a pair. Expected: 2, Got: 3.";
  assert([[error reason] isEqualToString:reason]);
}
std::set
#include <set>
#include <string>
std::set<std::string> getLanguages() {
    return {"English", "Spanish"};
}
// std::set corresponds to NSOrderedSet
NSOrderedSet* languages = [m getLanguages];
assert([languages count] == 2);
assert([languages containsObject:@"English"]);
assert([languages containsObject:@"Spanish"]);
std::tuple
#include <string>
#include <tuple>
std::tuple<int, std::string> sumInts(std::tuple<int, int, std::string> t) {
  // Sum the first two elements
  return {
    std::get<0>(t) + std::get<1>(t),
    std::get<2>(t)
  };
}
// std::tuple corresponds to a NSArray
// with the same amount of values
NSArray* toSum = [NSArray
  arrayWithObjects:@(1), @(2), @"Hello", nil];
assert([toSum count] == 3);
// Sending a tuple to a function
NSArray* summed = [m sumInts:toSum];
assert([summed count] == 2);
assert([[summed objectAtIndex:0] intValue] == 3);
assert([[summed objectAtIndex:1] isEqualToString:@"Hello"]);
// Error handling
@try {
  // Sending an array with size != 3
  NSArray* tooManyArgs =
    [toSum arrayByAddingObject:@"Boom"];
  [m sumInts:tooManyArgs];
  // Should throw exception before
  assert(NO);
} @catch(NSException* error) {
  assert([[error name] isEqualToString:@"TypeException"]);
  NSString* reason =
    @"The array passed does not match the number of types expected in the tuple. Expected: 3, Got: 4.";
  assert([[error reason] isEqualToString:reason]);
}
std::unordered_map
#include <string>
#include <unordered_map>
std::unordered_map<std::string, int>
getUnordered() {
  return {{"Unordered", 1}};
}
// std::unordered_map translates to a NSDictionary
NSDictionary* dict = [m getUnordered];
assert([dict count] == 1);
NSNumber* n = [dict objectForKey:@"Unordered"];
assert(n != nil);
assert([n intValue] == 1);
std::unordered_set
#include <string>
#include <unordered_set>
std::unordered_set<std::string> getLanguages() {
    return {"C++", "Objective-C"};
}
// std::unordered_set corresponds to NSSet
NSSet* languages = [m getLanguages];
assert([languages count] == 2);
assert([languages containsObject:@"C++"]);
assert([languages containsObject:@"Objective-C"]);
std::valarray
#include <valarray>
std::valarray<int> getIt() {
    return {0, 1, 2};
}
// std::valarray corresponds to NSArray
NSArray* v = [m getIt];
assert([v count] == 3);
// The vector contains {0, 1, 2}
assert([[v objectAtIndex:0] intValue] == 0);
assert([[v objectAtIndex:1] intValue] == 1);
assert([[v objectAtIndex:2] intValue] == 2);
std::vector
#include <algorithm>
#include <vector>
std::vector<int> f() {
  return {0, 1, 2};
}
bool allOf(std::vector<bool> const& conditions) {
  return std::all_of(
      conditions.begin(), conditions.end(),
      [](auto c) { return c; });
}
double sum(std::vector<double> const& numbers) {
  double sum = 0;
  for (double number : numbers) {
    sum += number;
  }
  return sum;
}
// std::vector corresponds to NSArray
NSArray* v = [m f];
assert([v count] == 3);
// The vector contains {0, 1, 2}
assert([[v objectAtIndex:0] intValue] == 0);
assert([[v objectAtIndex:1] intValue] == 1);
assert([[v objectAtIndex:2] intValue] == 2);
// Sending NSArray into function works as well
NSArray* conditions = @[@(YES), @(YES), @(NO)];
assert([m allOf:conditions] == NO);
NSArray<NSNumber*>* toSum = @[@(1.1), @(2.2), @(3.3)];
assert([m sum:toSum] == 6.6);