Initial commit
This commit is contained in:
commit
f222bb92af
9 changed files with 359 additions and 0 deletions
29
CMakeLists.txt
Normal file
29
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
cmake_minimum_required(VERSION 4.1)
|
||||||
|
|
||||||
|
project(
|
||||||
|
mini-obj-c
|
||||||
|
LANGUAGES
|
||||||
|
C
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
add_library(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
mini-obj-c.c
|
||||||
|
)
|
||||||
|
target_include_directories(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
set_target_properties(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
PROPERTIES
|
||||||
|
C_STANDARD 23
|
||||||
|
C_STANDARD_REQUIRED ON
|
||||||
|
C_EXTENSIONS OFF
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(example)
|
||||||
|
|
||||||
24
example/CMakeLists.txt
Normal file
24
example/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
add_executable(
|
||||||
|
example
|
||||||
|
main.c
|
||||||
|
MObject.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(
|
||||||
|
example
|
||||||
|
PROPERTIES
|
||||||
|
C_STANDARD 23
|
||||||
|
C_STANDARD_REQUIRED ON
|
||||||
|
C_EXTENSIONS OFF
|
||||||
|
)
|
||||||
|
target_link_libraries(
|
||||||
|
example
|
||||||
|
PRIVATE
|
||||||
|
${PROJECT_NAME}
|
||||||
|
)
|
||||||
|
target_include_directories(
|
||||||
|
example
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
86
example/MObject.c
Normal file
86
example/MObject.c
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
#include "MObject.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static struct Object* alloc(struct Object* self);
|
||||||
|
static struct Object* retain(struct Object* self);
|
||||||
|
static struct Object* release(struct Object* self);
|
||||||
|
|
||||||
|
struct Method MObject_meta_methods[] = {
|
||||||
|
{ .sel = "alloc", .impl = alloc },
|
||||||
|
};
|
||||||
|
struct Class MObjectMetaClass = {
|
||||||
|
.class = &MObjectMetaClass,
|
||||||
|
.name = "MObject(meta)",
|
||||||
|
.super = nullptr,
|
||||||
|
.methods = MObject_meta_methods,
|
||||||
|
.methods_count = sizeof(MObject_meta_methods) / sizeof(struct Method),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Method MObject_methods[] = {
|
||||||
|
{ .sel = "retain", .impl = retain },
|
||||||
|
{ .sel = "release", .impl = release },
|
||||||
|
};
|
||||||
|
struct Class MObject = {
|
||||||
|
.class = &MObjectMetaClass,
|
||||||
|
.name = "MObject",
|
||||||
|
.super = nullptr,
|
||||||
|
.methods = MObject_methods,
|
||||||
|
.methods_count = sizeof(MObject_methods) / sizeof(struct Method),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Header
|
||||||
|
{
|
||||||
|
size_t retain_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct Object* alloc(struct Object* self)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Class* class = (struct Class*)self;
|
||||||
|
|
||||||
|
auto size = sizeof(struct Header) + sizeof(struct Object);
|
||||||
|
void* mem = malloc(size);
|
||||||
|
memset(mem, 0, size);
|
||||||
|
|
||||||
|
struct Header* header = (struct Header*)mem;
|
||||||
|
header->retain_count = 1;
|
||||||
|
|
||||||
|
struct Object* obj = (struct Object*)((uintptr_t)mem + sizeof(struct Header));
|
||||||
|
obj->class = class;
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct Object* retain(struct Object* self)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Header* header = (struct Header*)((uintptr_t)self - sizeof(struct Header));
|
||||||
|
header->retain_count += 1;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct Object* release(struct Object* self)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Header* header = (struct Header*)((uintptr_t)self - sizeof(struct Header));
|
||||||
|
header->retain_count -= 1;
|
||||||
|
if (header->retain_count <= 0) {
|
||||||
|
free(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
5
example/MObject.h
Normal file
5
example/MObject.h
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include <mini-obj-c.h>
|
||||||
|
|
||||||
|
extern struct Class MObjectMetaClass;
|
||||||
|
extern struct Class MObject;
|
||||||
|
|
||||||
55
example/main.c
Normal file
55
example/main.c
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <mini-obj-c.h>
|
||||||
|
|
||||||
|
#include "MObject.h"
|
||||||
|
|
||||||
|
static struct Object* static_method(struct Object* self);
|
||||||
|
static struct Object* instance_method(struct Object* self);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
struct Method Test_meta_methods[] = {
|
||||||
|
{ .sel = "staticMethod", .impl = static_method },
|
||||||
|
};
|
||||||
|
struct Class TestMetaClass = {
|
||||||
|
.class = &TestMetaClass,
|
||||||
|
.name = "Test(meta)",
|
||||||
|
.super = &MObjectMetaClass,
|
||||||
|
.methods = Test_meta_methods,
|
||||||
|
.methods_count = sizeof(Test_meta_methods) / sizeof(struct Method),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Method Test_methods[] = {
|
||||||
|
{ .sel = "instanceMethod", .impl = instance_method },
|
||||||
|
};
|
||||||
|
struct Class Test = {
|
||||||
|
.class = &TestMetaClass,
|
||||||
|
.name = "Test",
|
||||||
|
.super = &MObject,
|
||||||
|
.methods = Test_methods,
|
||||||
|
.methods_count = sizeof(Test_methods) / sizeof(struct Method),
|
||||||
|
};
|
||||||
|
|
||||||
|
msg_send((struct Object*)&Test, "staticMethod");
|
||||||
|
|
||||||
|
struct Object* obj = msg_send((struct Object*)&Test, "alloc");
|
||||||
|
msg_send(obj, "instanceMethod");
|
||||||
|
msg_send(obj, "release");
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct Object* static_method(struct Object* self)
|
||||||
|
{
|
||||||
|
struct Class* class = (struct Class*)self;
|
||||||
|
printf("Hello from class '%s'\n", class->name);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct Object* instance_method(struct Object* self)
|
||||||
|
{
|
||||||
|
printf("Hello from instance of '%s'\n", self->class->name);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": [
|
||||||
|
"systems"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1791914995,
|
||||||
|
"narHash": "sha256-Zf/xfQEAasnwnmEfHqW/4hjDE/s44yn2hD4RuVIZRjA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b7a79d3fea1e214f6d668d521cecec0c01e26544",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1792116957,
|
||||||
|
"narHash": "sha256-MTJCMrhDFb+GRbLxBPTp8kB4dDlO+egmQ5qsJgYuaWw=",
|
||||||
|
"rev": "f20cd90e28fe0403873f473b29e42662cd86e8c4",
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1093421.f20cd90e28fe/nixexprs.tar.zst"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1680978846,
|
||||||
|
"narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "x86_64-linux",
|
||||||
|
"rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "x86_64-linux",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
35
flake.nix
Normal file
35
flake.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
description = "Mini Obj-C";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "nixpkgs";
|
||||||
|
systems.url = "github:nix-systems/x86_64-linux";
|
||||||
|
flake-utils = {
|
||||||
|
url = "github:numtide/flake-utils";
|
||||||
|
inputs.systems.follows = "systems";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, ... }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
pname = "mini-obj-c";
|
||||||
|
version = "0.0.1";
|
||||||
|
src = ./.;
|
||||||
|
buildInputs = with pkgs; [ ];
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
ninja
|
||||||
|
cmake
|
||||||
|
clang-tools
|
||||||
|
];
|
||||||
|
in {
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
inherit buildInputs nativeBuildInputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
packages.default = pkgs.stdenv.mkDerivation {
|
||||||
|
inherit buildInputs nativeBuildInputs pname version src;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
37
mini-obj-c.c
Normal file
37
mini-obj-c.c
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "mini-obj-c.h"
|
||||||
|
|
||||||
|
#define WARN(f, ...) fprintf(stderr, f "\n", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
MethodImplementation method_lookup(struct Class* class, const char* sel)
|
||||||
|
{
|
||||||
|
for (; class; class = class->super) {
|
||||||
|
for (size_t i = 0; i < class->methods_count; ++i) {
|
||||||
|
auto entry = class->methods[i];
|
||||||
|
if (strcmp(entry.sel, sel) == 0) {
|
||||||
|
return entry.impl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Object* msg_send(struct Object* self, const char* sel)
|
||||||
|
{
|
||||||
|
if (!self || !self->class) {
|
||||||
|
WARN("Invalid object");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto impl = method_lookup(self->class, sel);
|
||||||
|
if (impl) {
|
||||||
|
return impl(self);
|
||||||
|
} else {
|
||||||
|
WARN("Method '%s' not found for class '%s'", sel, self->class->name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
27
mini-obj-c.h
Normal file
27
mini-obj-c.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct Object
|
||||||
|
{
|
||||||
|
struct Class* class;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Class
|
||||||
|
{
|
||||||
|
struct Class* class;
|
||||||
|
|
||||||
|
const char* name;
|
||||||
|
struct Class* super;
|
||||||
|
struct Method* methods;
|
||||||
|
size_t methods_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Object* (*MethodImplementation)(struct Object* self);
|
||||||
|
|
||||||
|
struct Method
|
||||||
|
{
|
||||||
|
const char* sel;
|
||||||
|
MethodImplementation impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Object* msg_send(struct Object* self, const char* sel);
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue