From 4902288a5a7885d348cdf04ac33768783db08e31 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 5 Aug 2024 22:04:16 +0200 Subject: [PATCH 1/2] Add `reflect.struct_field_count` that returns the number of fields in a struct type Example: ```odin package struct_count_example import "core:fmt" import "core:reflect" import "core:dynlib" Foo :: struct{ one: int, two: f32, } Bar :: struct { three: int, four: bool, five: f64, } Game_Api :: struct { init: proc(api: ^Game_Api), update: proc(api: ^Game_Api), using foo: Foo, bar: Bar, // Private stuff reload_count: int, __handle: rawptr, } API_PRIVATE_COUNT :: 2 game_api: Game_Api main :: proc() { fmt.printfln("Game_Api, .Top: %v", reflect.struct_field_count(Game_Api)) // 6 fmt.printfln("Game_Api, .Using: %v", reflect.struct_field_count(Game_Api, .Using)) // 8 fmt.printfln("Game_Api, .Recursive: %v", reflect.struct_field_count(Game_Api, .Recursive)) // 11 symbols_loaded, _ := dynlib.initialize_symbols(&game_api, "game.dll") symbols_expected := reflect.struct_field_count(Game_Api) - API_PRIVATE_COUNT if symbols_loaded == -1 { fmt.eprintln("Couldn't load game.dll") return } else if symbols_loaded != symbols_expected { fmt.eprintfln("Expected %v symbols, got %v", symbols_expected, symbols_loaded) return } } ``` --- core/reflect/reflect.odin | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 23c0f803e..9a5551d42 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -496,6 +496,62 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr { return nil } +Struct_Field_Count_Method :: enum { + Top_Level, + Using, + Recursive, +} + +/* +Counts the number of fields in a struct + +This procedure returns the number of fields in a struct, counting in one of three ways: +- .Top_Level: Only counts the top-level fields +- .Using: Same count as .Top_Level, and adds the field count of any `using s: Struct` it encounters (in addition to itself) +- .Recursive: The count of all top-level fields, plus the count of any child struct's fields, recursively + +Inputs: +- T: The struct type +- method: The counting method + +Returns: +- The `count`, enumerated using the `method`, which will be `0` if the type is not a struct + +Example: + expected_count := reflect.struct_field_count(Game_API) - API_PRIVATE_COUNT + if symbols_loaded != expected_count { + fmt.eprintf("Expected %v symbols, got %v", expected_count, symbols_loaded) + return + } +*/ +@(require_results) +struct_field_count :: proc(T: typeid, method := Struct_Field_Count_Method.Top_Level) -> (count: int) { + ti := runtime.type_info_base(type_info_of(T)) + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + switch method { + case .Top_Level: + return int(s.field_count) + + case .Using: + count = int(s.field_count) + for type, i in s.types[:s.field_count] { + if s.usings[i] { + count += struct_field_count(type.id) + } + } + + case .Recursive: + count = int(s.field_count) + for type in s.types[:s.field_count] { + count += struct_field_count(type.id) + } + + case: return 0 + } + } + return +} + @(require_results) struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) { ti := runtime.type_info_base(type_info_of(T)) From 17ebaffce8f97b895c651bf57567b13f2685f849 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 5 Aug 2024 22:12:22 +0200 Subject: [PATCH 2/2] Update comment. --- core/reflect/reflect.odin | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 9a5551d42..aff82136a 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -518,9 +518,11 @@ Returns: - The `count`, enumerated using the `method`, which will be `0` if the type is not a struct Example: - expected_count := reflect.struct_field_count(Game_API) - API_PRIVATE_COUNT - if symbols_loaded != expected_count { - fmt.eprintf("Expected %v symbols, got %v", expected_count, symbols_loaded) + symbols_loaded, ok := dynlib.initialize_symbols(&game_api, "game.dll") + symbols_expected := reflect.struct_field_count(Game_Api) - API_PRIVATE_COUNT + + if symbols_loaded != symbols_expected { + fmt.eprintf("Expected %v symbols, got %v", symbols_expected, symbols_loaded) return } */