diff --git a/.clang-format b/.clang-format index b042635392c5..bfcd93ac8f2f 100644 --- a/.clang-format +++ b/.clang-format @@ -12,10 +12,12 @@ AlwaysBreakBeforeMultilineStrings: true BinPackArguments: false ColumnLimit: 100 ContinuationIndentWidth: 4 +EmptyLineBeforeAccessModifier: LogicalBlock IndentWidth: 4 IndentWrappedFunctionNames: true InsertTrailingCommas: Wrapped MaxEmptyLinesToKeep: 1 NamespaceIndentation: All PointerAlignment: Left +SeparateDefinitionBlocks: Always SpacesBeforeTrailingComments: 1 diff --git a/crates/re_types/source_hash.txt b/crates/re_types/source_hash.txt index b70176ddaae5..0fef79c14a36 100644 --- a/crates/re_types/source_hash.txt +++ b/crates/re_types/source_hash.txt @@ -1,4 +1,4 @@ # This is a sha256 hash for all direct and indirect dependencies of this crate's build script. # It can be safely removed at anytime to force the build script to run again. # Check out build.rs to see how it's computed. -d5ccdc80e148c8058d3885418f9c701fbae4a5fbb92defb9bd625ad27ed97a25 +289571cd5bcef516a43ead1678f39054c300890396d13933dc26d9294a07dffe diff --git a/crates/re_types_builder/src/arrow_registry.rs b/crates/re_types_builder/src/arrow_registry.rs index 2e92eebca8f6..0fdece3ab60a 100644 --- a/crates/re_types_builder/src/arrow_registry.rs +++ b/crates/re_types_builder/src/arrow_registry.rs @@ -300,7 +300,7 @@ impl From for LazyDatatype { DataType::Extension(name, datatype, metadata) => { LazyDatatype::Extension(name, Box::new((*datatype).into()), metadata) } - _ => unimplemented!("{datatype:#?}"), // NOLINT + _ => unimplemented!("{datatype:#?}"), } } } diff --git a/crates/re_types_builder/src/codegen/cpp.rs b/crates/re_types_builder/src/codegen/cpp.rs index 5e2e274e2759..12d484bbff4e 100644 --- a/crates/re_types_builder/src/codegen/cpp.rs +++ b/crates/re_types_builder/src/codegen/cpp.rs @@ -2,13 +2,69 @@ use std::collections::BTreeSet; use anyhow::Context as _; use camino::{Utf8Path, Utf8PathBuf}; -use proc_macro2::TokenStream; +use itertools::Itertools; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use rayon::prelude::*; -use crate::{codegen::AUTOGEN_WARNING, ArrowRegistry, ObjectKind, Objects}; +use crate::{ + codegen::AUTOGEN_WARNING, ArrowRegistry, Docs, ElementType, ObjectField, ObjectKind, Objects, + Type, +}; -const NEWLINE_TOKEN: &str = "RE_TOKEN_NEWLINE"; +// Special strings we insert as tokens, then search-and-replace later. +// This is so that we can insert comments and whitespace into the generated code. +// `TokenStream` ignores whitespace (including comments), but we can insert "quoted strings", +// so that is what we do. +const NEWLINE_TOKEN: &str = "NEWLINE_TOKEN"; +const NORMAL_COMMENT_PREFIX_TOKEN: &str = "NORMAL_COMMENT_PREFIX_TOKEN"; +const NORMAL_COMMENT_SUFFIX_TOKEN: &str = "NORMAL_COMMENT_SUFFIX_TOKEN"; +const DOC_COMMENT_PREFIX_TOKEN: &str = "DOC_COMMENT_PREFIX_TOKEN"; +const DOC_COMMENT_SUFFIX_TOKEN: &str = "DOC_COMMENT_SUFFIX_TOKEN"; +const TODO_TOKEN: &str = "TODO_TOKEN"; + +fn comment(text: &str) -> TokenStream { + quote! { #NORMAL_COMMENT_PREFIX_TOKEN #text #NORMAL_COMMENT_SUFFIX_TOKEN } +} + +fn doc_comment(text: &str) -> TokenStream { + quote! { #DOC_COMMENT_PREFIX_TOKEN #text #DOC_COMMENT_SUFFIX_TOKEN } +} + +fn string_from_token_stream(token_stream: &TokenStream, source_path: Option<&Utf8Path>) -> String { + let mut code = String::new(); + code.push_str(&format!("// {AUTOGEN_WARNING}\n")); + if let Some(source_path) = source_path { + code.push_str(&format!("// Based on {source_path:?}\n")); + } + + code.push('\n'); + code.push_str( + &token_stream + .to_string() + .replace(&format!("{NEWLINE_TOKEN:?}"), "\n") + .replace(&format!("{NORMAL_COMMENT_PREFIX_TOKEN:?} \""), "//") + .replace(&format!("\" {NORMAL_COMMENT_SUFFIX_TOKEN:?}"), "\n") + .replace(&format!("{DOC_COMMENT_PREFIX_TOKEN:?} \""), "///") + .replace(&format!("\" {DOC_COMMENT_SUFFIX_TOKEN:?}"), "\n") + .replace( + &format!("{TODO_TOKEN:?}"), + "\n// TODO(#2647): code-gen for C++\n", + ) + .replace("< ", "<") + .replace(" >", ">") + .replace(" ::", "::"), + ); + code.push('\n'); + + // clang_format has a bit of an ugly API: https://github.com/KDAB/clang-format-rs/issues/3 + clang_format::CLANG_FORMAT_STYLE + .set(clang_format::ClangFormatStyle::File) + .ok(); + code = clang_format::clang_format(&code).expect("Failed to run clang-format"); + + code +} pub struct CppCodeGenerator { output_path: Utf8PathBuf, @@ -62,7 +118,7 @@ impl CppCodeGenerator { .map(|obj| format!("{folder_name}/{}.hpp", obj.snake_case_name())); let tokens = quote! { #pragma_once - #(#hash include #header_file_names "RE_TOKEN_NEWLINE")* + #(#hash include #header_file_names "NEWLINE_TOKEN")* }; let filepath = folder_path .parent() @@ -96,30 +152,6 @@ impl crate::CodeGenerator for CppCodeGenerator { } } -fn string_from_token_stream(token_stream: &TokenStream, source_path: Option<&Utf8Path>) -> String { - let mut code = String::new(); - code.push_str(&format!("// {AUTOGEN_WARNING}\n")); - if let Some(source_path) = source_path { - code.push_str(&format!("// Based on {source_path:?}\n")); - } - - code.push('\n'); - code.push_str( - &token_stream - .to_string() - .replace(&format!("{NEWLINE_TOKEN:?}"), "\n"), - ); - code.push('\n'); - - // clang_format has a bit of an ugly API: https://github.com/KDAB/clang-format-rs/issues/3 - clang_format::CLANG_FORMAT_STYLE - .set(clang_format::ClangFormatStyle::File) - .ok(); - code = clang_format::clang_format(&code).expect("Failed to run clang-format"); - - code -} - fn write_file(filepath: &Utf8PathBuf, code: String) { if let Ok(existing) = std::fs::read_to_string(filepath) { if existing == code { @@ -134,29 +166,24 @@ fn write_file(filepath: &Utf8PathBuf, code: String) { } fn generate_hpp_cpp( - _objects: &Objects, + objects: &Objects, _arrow_registry: &ArrowRegistry, obj: &crate::Object, ) -> (TokenStream, TokenStream) { - let obj_kind_ident = format_ident!("{}", obj.kind.plural_snake_case()); - - let pascal_case_name = &obj.name; - let pascal_case_ident = format_ident!("{pascal_case_name}"); + let QuotedObject { hpp, cpp } = QuotedObject::new(objects, obj); let snake_case_name = obj.snake_case_name(); - let hash = quote! { # }; let pragma_once = pragma_once(); let header_file_name = format!("{snake_case_name}.hpp"); let hpp = quote! { #pragma_once - namespace rr { - namespace #obj_kind_ident { - struct #pascal_case_ident { }; - } - } + #hpp + }; + let cpp = quote! { + #hash include #header_file_name #NEWLINE_TOKEN #NEWLINE_TOKEN + #cpp }; - let cpp = quote! { #hash include #header_file_name }; (hpp, cpp) } @@ -167,3 +194,601 @@ fn pragma_once() -> TokenStream { #hash pragma once #NEWLINE_TOKEN #NEWLINE_TOKEN } } + +struct QuotedObject { + hpp: TokenStream, + cpp: TokenStream, +} + +impl QuotedObject { + pub fn new(objects: &Objects, obj: &crate::Object) -> Self { + match obj.specifics { + crate::ObjectSpecifics::Struct => Self::from_struct(objects, obj), + crate::ObjectSpecifics::Union { .. } => Self::from_union(objects, obj), + } + } + + fn from_struct(_objects: &Objects, obj: &crate::Object) -> QuotedObject { + let namespace_ident = format_ident!("{}", obj.kind.plural_snake_case()); // `datatypes`, `components`, or `archetypes` + let pascal_case_name = &obj.name; + let pascal_case_ident = format_ident!("{pascal_case_name}"); // The PascalCase name of the object type. + let quoted_docs = quote_docstrings(&obj.docs); + + let mut hpp_includes = Includes::default(); + + let field_declarations = obj + .fields + .iter() + .map(|obj_field| { + let declaration = quote_declaration_with_docstring( + &mut hpp_includes, + obj_field, + &format_ident!("{}", obj_field.name), + ); + quote! { + #NEWLINE_TOKEN + #declaration + } + }) + .collect_vec(); + + let constructor = if obj.fields.len() == 1 { + // Single-field struct - it is a newtype wrapper. + // Create a implicit constructor from its own field-type. + let obj_field = &obj.fields[0]; + if let Type::Array { .. } = &obj_field.typ { + // TODO(emilk): implicit constructor for arrays + quote! {} + } else { + hpp_includes.system.insert("utility".to_owned()); // std::move + + let field_ident = format_ident!("{}", obj_field.name); + let parameter_declaration = + quote_declaration(&mut hpp_includes, obj_field, &field_ident); + quote! { + #pascal_case_ident(#parameter_declaration) : #field_ident(std::move(#field_ident)) {} + } + } + } else { + quote! {} + }; + + let hpp = quote! { + #hpp_includes + + namespace rr { + namespace #namespace_ident { + #quoted_docs + struct #pascal_case_ident { + #(#field_declarations;)* + + #constructor + }; + } + } + }; + + let cpp = quote! {}; // TODO(emilk): add Arrow serialization code here! + + Self { hpp, cpp } + } + + fn from_union(objects: &Objects, obj: &crate::Object) -> QuotedObject { + // We implement sum-types as tagged unions; + // Putting non-POD types in a union requires C++11. + // + // enum class Rotation3DTag { + // NONE = 0, + // Quaternion, + // AxisAngle, + // }; + // + // union Rotation3DData { + // Quaternion quaternion; + // AxisAngle axis_angle; + // }; + // + // struct Rotation3D { + // Rotation3DTag _tag; + // Rotation3DData _data; + // }; + + let namespace_ident = format_ident!("{}", obj.kind.plural_snake_case()); // `datatypes`, `components`, or `archetypes` + let pascal_case_name = &obj.name; + let pascal_case_ident = format_ident!("{pascal_case_name}"); // The PascalCase name of the object type. + let quoted_docs = quote_docstrings(&obj.docs); + + let tag_typename = format_ident!("{pascal_case_name}Tag"); + let data_typename = format_ident!("{pascal_case_name}Data"); + + let tag_fields = std::iter::once({ + let comment = doc_comment( + "Having a special empty state makes it possible to implement move-semantics. \ + We need to be able to leave the object in a state which we can run the destructor on."); + let tag_name = format_ident!("NONE"); + quote! { + #NEWLINE_TOKEN + #comment + #tag_name = 0, + } + }) + .chain(obj.fields.iter().map(|obj_field| { + let ident = format_ident!("{}", obj_field.name); + quote! { + #ident, + } + })) + .collect_vec(); + + let mut hpp_includes = Includes::default(); + + hpp_includes.system.insert("utility".to_owned()); // std::move + + let enum_data_declarations = obj + .fields + .iter() + .map(|obj_field| { + let declaration = quote_declaration_with_docstring( + &mut hpp_includes, + obj_field, + &format_ident!("{}", crate::to_snake_case(&obj_field.name)), + ); + quote! { + #NEWLINE_TOKEN + #declaration + } + }) + .collect_vec(); + + let implicit_constructors = if are_types_disjoint(&obj.fields) { + // Implicit construct from the different variant types: + obj.fields + .iter() + .map(|obj_field| { + let snake_case_ident = + format_ident!("{}", crate::to_snake_case(&obj_field.name)); + let docstring = quote_docstrings(&obj_field.docs); + let param_declaration = + quote_declaration(&mut hpp_includes, obj_field, &snake_case_ident); + quote! { + #docstring + #pascal_case_ident(#param_declaration) + { + *this = #pascal_case_ident::#snake_case_ident(std::move(#snake_case_ident)); + } + } + }) + .collect_vec() + } else { + // Cannot make implicit constructors, e.g. for + // `enum Angle { Radians(f32), Degrees(f32) };` + vec![] + }; + + let static_constructors = obj + .fields + .iter() + .map(|obj_field| { + quote_static_constructor_for_enum_type( + objects, + &mut hpp_includes, + obj_field, + &pascal_case_ident, + &tag_typename, + ) + }) + .collect_vec(); + + let destructor = if obj.has_default_destructor(objects) { + // No destructor needed + quote! {} + } else { + let destructor_match_arms = std::iter::once({ + let comment = comment("Nothing to destroy"); + quote! { + case detail::#tag_typename::NONE: { + break; #comment + } + } + }) + .chain(obj.fields.iter().map(|obj_field| { + let tag_ident = format_ident!("{}", obj_field.name); + let field_ident = format_ident!("{}", crate::to_snake_case(&obj_field.name)); + + if obj_field.typ.has_default_destructor(objects) { + let comment = comment("has a trivial destructor"); + quote! { + case detail::#tag_typename::#tag_ident: { + break; #comment + } + } + } else if let Type::Array { elem_type, length } = &obj_field.typ { + // We need special casing for destroying arrays in C++: + let elem_type = quote_element_type(&mut hpp_includes, elem_type); + let length = proc_macro2::Literal::usize_unsuffixed(*length); + quote! { + case detail::#tag_typename::#tag_ident: { + typedef #elem_type TypeAlias; + for (size_t i = #length; i > 0; i -= 1) { + _data.#field_ident[i-1].~TypeAlias(); + } + break; + } + } + } else { + let typedef_declaration = quote_declaration( + &mut hpp_includes, + obj_field, + &format_ident!("TypeAlias"), + ); + hpp_includes.system.insert("utility".to_owned()); // std::move + quote! { + case detail::#tag_typename::#tag_ident: { + typedef #typedef_declaration; + _data.#field_ident.~TypeAlias(); + break; + } + } + } + })) + .collect_vec(); + + quote! { + ~#pascal_case_ident() { + switch (this->_tag) { + #(#destructor_match_arms)* + } + } + } + }; + + hpp_includes.system.insert("cstring".to_owned()); // std::memcpy + + let swap_comment = comment("This bitwise swap would fail for self-referential types, but we don't have any of those."); + + let hpp = quote! { + #hpp_includes + + namespace rr { + namespace #namespace_ident { + namespace detail { + enum class #tag_typename { + #(#tag_fields)* + }; + + union #data_typename { + #(#enum_data_declarations;)* + + #data_typename() { } // Required by static constructors + ~#data_typename() { } + + void swap(#data_typename& other) noexcept { + #NEWLINE_TOKEN + #swap_comment + char temp[sizeof(#data_typename)]; + std::memcpy(temp, this, sizeof(#data_typename)); + std::memcpy(this, &other, sizeof(#data_typename)); + std::memcpy(&other, temp, sizeof(#data_typename)); + } + }; + + } + + #quoted_docs + struct #pascal_case_ident { + private: + detail::#tag_typename _tag; + detail::#data_typename _data; + + // Empty state required by static constructors: + #pascal_case_ident() : _tag(detail::#tag_typename::NONE) {} + + public: + // Move-constructor: + #pascal_case_ident(#pascal_case_ident&& other) noexcept : _tag(detail::#tag_typename::NONE) { + this->swap(other); + } + + // Move-assignment: + #pascal_case_ident& operator=(#pascal_case_ident&& other) noexcept { + this->swap(other); + return *this; + } + + #destructor + + #(#static_constructors)* + + #(#implicit_constructors)* + + // This is useful for easily implementing the move constructor and move assignment operator: + void swap(#pascal_case_ident& other) noexcept { + // Swap tags: + auto tag_temp = this->_tag; + this->_tag = other._tag; + other._tag = tag_temp; + + // Swap data: + this->_data.swap(other._data); + } + }; + } + } + }; + + let cpp = quote! {}; // TODO(emilk): add Arrow serialization code here! + + Self { hpp, cpp } + } +} + +/// e.g. `static Angle radians(float radians);` -> `auto angle = Angle::radians(radians);` +fn quote_static_constructor_for_enum_type( + objects: &Objects, + hpp_includes: &mut Includes, + obj_field: &ObjectField, + pascal_case_ident: &Ident, + tag_typename: &Ident, +) -> TokenStream { + let tag_ident = format_ident!("{}", obj_field.name); + let snake_case_ident = format_ident!("{}", crate::to_snake_case(&obj_field.name)); + let docstring = quote_docstrings(&obj_field.docs); + + let param_declaration = quote_declaration(hpp_includes, obj_field, &snake_case_ident); + + if let Type::Array { elem_type, length } = &obj_field.typ { + // We need special casing for constructing arrays: + let length = proc_macro2::Literal::usize_unsuffixed(*length); + + let element_assignment = if elem_type.has_default_destructor(objects) { + // Generate simpoler code for simple types: + quote! { + self._data.#snake_case_ident[i] = std::move(#snake_case_ident[i]); + } + } else { + // We need to use placement-new since the union is in an uninitialized state here: + hpp_includes.system.insert("new".to_owned()); // placement-new + quote! { + new (&self._data.#snake_case_ident[i]) TypeAlias(std::move(#snake_case_ident[i])); + } + }; + + let elem_type = quote_element_type(hpp_includes, elem_type); + + quote! { + #docstring + static #pascal_case_ident #snake_case_ident(#param_declaration) + { + typedef #elem_type TypeAlias; + #pascal_case_ident self; + self._tag = detail::#tag_typename::#tag_ident; + for (size_t i = 0; i < #length; i += 1) { + #element_assignment + } + return std::move(self); + } + } + } else if obj_field.typ.has_default_destructor(objects) { + // Generate simpoler code for simple types: + quote! { + #docstring + static #pascal_case_ident #snake_case_ident(#param_declaration) + { + #pascal_case_ident self; + self._tag = detail::#tag_typename::#tag_ident; + self._data.#snake_case_ident = std::move(#snake_case_ident); + return std::move(self); + } + } + } else { + // We need to use placement-new since the union is in an uninitialized state here: + hpp_includes.system.insert("new".to_owned()); // placement-new + let typedef_declaration = + quote_declaration(hpp_includes, obj_field, &format_ident!("TypeAlias")); + quote! { + #docstring + static #pascal_case_ident #snake_case_ident(#param_declaration) + { + typedef #typedef_declaration; + #pascal_case_ident self; + self._tag = detail::#tag_typename::#tag_ident; + new (&self._data.#snake_case_ident) TypeAlias(std::move(#snake_case_ident)); + return std::move(self); + } + } + } +} + +fn are_types_disjoint(fields: &[ObjectField]) -> bool { + let type_set: std::collections::HashSet<&Type> = fields.iter().map(|f| &f.typ).collect(); + type_set.len() == fields.len() +} + +/// Keep track of necessary includes for a file. +struct Includes { + /// `#include ` etc + system: BTreeSet, + + /// `#include datatypes.hpp"` etc + local: BTreeSet, +} + +impl Default for Includes { + fn default() -> Self { + let mut slf = Self { + system: BTreeSet::new(), + local: BTreeSet::new(), + }; + slf.system.insert("cstdint".to_owned()); // we use `uint32_t` etc everywhere. + slf + } +} + +impl quote::ToTokens for Includes { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { system, local } = self; + + let hash = quote! { # }; + let system = system.iter().map(|name| { + let name = format_ident!("{}", name); + quote! { #hash include <#name> #NEWLINE_TOKEN } + }); + let local = local.iter().map(|name| { + quote! { #hash include #name #NEWLINE_TOKEN } + }); + + quote! { + #(#system)* + #NEWLINE_TOKEN + #(#local)* + #NEWLINE_TOKEN + #NEWLINE_TOKEN + } + .to_tokens(tokens); + } +} + +fn quote_declaration_with_docstring( + includes: &mut Includes, + obj_field: &ObjectField, + name: &syn::Ident, +) -> TokenStream { + let quoted = quote_declaration(includes, obj_field, name); + + let docstring = quote_docstrings(&obj_field.docs); + + let quoted = quote! { + #docstring + #quoted + }; + + quoted +} + +fn quote_declaration( + includes: &mut Includes, + obj_field: &ObjectField, + name: &syn::Ident, +) -> TokenStream { + if obj_field.is_nullable { + includes.system.insert("optional".to_owned()); + match &obj_field.typ { + Type::UInt8 => quote! { std::optional #name }, + Type::UInt16 => quote! { std::optional #name }, + Type::UInt32 => quote! { std::optional #name }, + Type::UInt64 => quote! { std::optional #name }, + Type::Int8 => quote! { std::optional #name }, + Type::Int16 => quote! { std::optional #name }, + Type::Int32 => quote! { std::optional #name }, + Type::Int64 => quote! { std::optional #name }, + Type::Bool => quote! { std::optional #name }, + Type::Float16 => unimplemented!("float16 not yet implemented for C++"), + Type::Float32 => quote! { std::optional #name }, + Type::Float64 => quote! { std::optional #name }, + Type::String => { + includes.system.insert("string".to_owned()); + quote! { std::optional #name } + } + Type::Array { .. } => { + unimplemented!( + "Optional fixed-size array not yet implemented in C++. {:#?}", + obj_field.typ + ) + } + Type::Vector { elem_type } => { + let elem_type = quote_element_type(includes, elem_type); + includes.system.insert("vector".to_owned()); + quote! { std::optional> #name } + } + Type::Object(fqname) => { + let type_name = quote_fqname_as_type_path(includes, fqname); + quote! { std::optional<#type_name> #name } + } + } + } else { + match &obj_field.typ { + Type::UInt8 => quote! { uint8_t #name }, + Type::UInt16 => quote! { uint16_t #name }, + Type::UInt32 => quote! { uint32_t #name }, + Type::UInt64 => quote! { uint64_t #name }, + Type::Int8 => quote! { int8_t #name }, + Type::Int16 => quote! { int16_t #name }, + Type::Int32 => quote! { int32_t #name }, + Type::Int64 => quote! { int64_t #name }, + Type::Bool => quote! { bool #name }, + Type::Float16 => unimplemented!("float16 not yet implemented for C++"), + Type::Float32 => quote! { float #name }, + Type::Float64 => quote! { double #name }, + Type::String => { + includes.system.insert("string".to_owned()); + quote! { std::string #name } + } + Type::Array { elem_type, length } => { + let elem_type = quote_element_type(includes, elem_type); + let length = proc_macro2::Literal::usize_unsuffixed(*length); + + quote! { #elem_type #name[#length] } + } + Type::Vector { elem_type } => { + let elem_type = quote_element_type(includes, elem_type); + includes.system.insert("vector".to_owned()); + quote! { std::vector<#elem_type> #name } + } + Type::Object(fqname) => { + let type_name = quote_fqname_as_type_path(includes, fqname); + quote! { #type_name #name } + } + } + } +} + +fn quote_element_type(includes: &mut Includes, typ: &ElementType) -> TokenStream { + match typ { + ElementType::UInt8 => quote! { uint8_t }, + ElementType::UInt16 => quote! { uint16_t }, + ElementType::UInt32 => quote! { uint32_t }, + ElementType::UInt64 => quote! { uint64_t }, + ElementType::Int8 => quote! { int8_t }, + ElementType::Int16 => quote! { int16_t }, + ElementType::Int32 => quote! { int32_t }, + ElementType::Int64 => quote! { int64_t }, + ElementType::Bool => quote! { bool }, + ElementType::Float16 => unimplemented!("float16 not yet implemented for C++"), + ElementType::Float32 => quote! { float }, + ElementType::Float64 => quote! { double }, + ElementType::String => { + includes.system.insert("string".to_owned()); + quote! { std::string } + } + ElementType::Object(fqname) => quote_fqname_as_type_path(includes, fqname), + } +} + +fn quote_fqname_as_type_path(includes: &mut Includes, fqname: &str) -> TokenStream { + let fqname = fqname + .replace(".testing", "") + .replace('.', "::") + .replace("crate", "rr") + .replace("rerun", "rr"); + + // fqname example: "rr::datatypes::Transform3D" + let components = fqname.split("::").collect::>(); + if let ["rr", obj_kind, typname] = &components[..] { + includes.local.insert(format!( + "../{obj_kind}/{}.hpp", + crate::to_snake_case(typname) + )); + } + + let expr: syn::TypePath = syn::parse_str(&fqname).unwrap(); + quote!(#expr) +} + +fn quote_docstrings(docs: &Docs) -> TokenStream { + let lines = crate::codegen::get_documentation(docs, &["cpp", "c++"]); + let quoted_lines = lines.iter().map(|docstring| doc_comment(docstring)); + quote! { + #NEWLINE_TOKEN + #(#quoted_lines)* + } +} diff --git a/crates/re_types_builder/src/codegen/python.rs b/crates/re_types_builder/src/codegen/python.rs index 8ac6c8b41963..bf4674256b60 100644 --- a/crates/re_types_builder/src/codegen/python.rs +++ b/crates/re_types_builder/src/codegen/python.rs @@ -46,7 +46,7 @@ impl PythonObjectExt for Object { self.is_delegating_component() .then(|| { if let Type::Object(name) = &self.fields[0].typ { - Some(objects.get(name)) + Some(&objects[name]) } else { None } @@ -597,7 +597,7 @@ impl QuotedObject { ObjectKind::Component => { // a component might be either delegating to a datatype or using a native type if let Type::Object(ref dtype_fqname) = obj.fields[0].typ { - let dtype_obj = objects.get(dtype_fqname); + let dtype_obj = &objects[dtype_fqname]; code.push_text( quote_arrow_support_from_delegating_component(obj, dtype_obj), 1, @@ -1136,7 +1136,7 @@ fn quote_field_converter_from_field( }, Type::Object(fqname) => { let typ = quote_type_from_element_type(&ElementType::Object(fqname.clone())); - let field_obj = objects.get(fqname); + let field_obj = &objects[fqname]; // we generate a default converter only if the field's type can be constructed with a // single argument @@ -1394,7 +1394,7 @@ fn quote_arrow_datatype(datatype: &DataType) -> String { DataType::Extension(_, datatype, _) => quote_arrow_datatype(datatype), - _ => unimplemented!("{datatype:#?}"), // NOLINT + _ => unimplemented!("{datatype:#?}"), } } diff --git a/crates/re_types_builder/src/codegen/rust.rs b/crates/re_types_builder/src/codegen/rust.rs index 215fc5245d14..41e194b2418d 100644 --- a/crates/re_types_builder/src/codegen/rust.rs +++ b/crates/re_types_builder/src/codegen/rust.rs @@ -547,17 +547,22 @@ fn quote_doc_from_docs(docs: &Docs) -> TokenStream { /// becomes just `String`. /// The returned boolean indicates whether there was anything to unwrap at all. fn quote_field_type_from_field(obj_field: &ObjectField, unwrap: bool) -> (TokenStream, bool) { - let obj_field_type = TypeTokenizer(&obj_field.typ, unwrap); + let obj_field_type = TypeTokenizer { + typ: &obj_field.typ, + unwrap, + }; let unwrapped = unwrap && matches!(obj_field.typ, Type::Array { .. } | Type::Vector { .. }); (quote!(#obj_field_type), unwrapped) } -/// `(type, unwrap)` -struct TypeTokenizer<'a>(&'a Type, bool); +struct TypeTokenizer<'a> { + typ: &'a Type, + unwrap: bool, +} impl quote::ToTokens for TypeTokenizer<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self(typ, unwrap) = self; + let Self { typ, unwrap } = self; match typ { Type::UInt8 => quote!(u8), Type::UInt16 => quote!(u16), @@ -568,7 +573,7 @@ impl quote::ToTokens for TypeTokenizer<'_> { Type::Int32 => quote!(i32), Type::Int64 => quote!(i64), Type::Bool => quote!(bool), - Type::Float16 => unimplemented!("{typ:#?}"), // NOLINT + Type::Float16 => unimplemented!("{typ:#?}"), Type::Float32 => quote!(f32), Type::Float64 => quote!(f64), Type::String => quote!(String), @@ -604,7 +609,7 @@ impl quote::ToTokens for &ElementType { ElementType::Int32 => quote!(i32), ElementType::Int64 => quote!(i64), ElementType::Bool => quote!(bool), - ElementType::Float16 => unimplemented!("{self:#?}"), // NOLINT + ElementType::Float16 => unimplemented!("{self:#?}"), ElementType::Float32 => quote!(f32), ElementType::Float64 => quote!(f64), ElementType::String => quote!(String), @@ -774,8 +779,7 @@ fn quote_trait_impls_from_obj( let component = quote!(crate::components::#component); let fqname = obj_field.typ.fqname().unwrap(); - let legacy_fqname = objects - .get(fqname) + let legacy_fqname = objects[fqname] .try_get_attr::(crate::ATTR_RERUN_LEGACY_FQNAME) .unwrap_or_else(|| fqname.to_owned()); @@ -1113,7 +1117,7 @@ impl quote::ToTokens for ArrowDataTypeTokenizer<'_> { quote!(DataType::Extension(#name.to_owned(), Box::new(#datatype), #metadata)) } - _ => unimplemented!("{:#?}", self.0), // NOLINT + _ => unimplemented!("{:#?}", self.0), } .to_tokens(tokens); } @@ -1461,7 +1465,7 @@ fn quote_arrow_serializer( ).boxed() }} } - _ => unimplemented!("{datatype:#?}"), // NOLINT + _ => unimplemented!("{datatype:#?}"), } } } @@ -1556,7 +1560,7 @@ fn quote_arrow_field_serializer( ); let inner_obj = if let DataType::Extension(fqname, _, _) = datatype { - Some(objects.get(fqname)) + Some(&objects[fqname]) } else { None }; @@ -1652,7 +1656,7 @@ fn quote_arrow_field_serializer( }} } - _ => unimplemented!("{datatype:#?}"), // NOLINT + _ => unimplemented!("{datatype:#?}"), } } @@ -1894,7 +1898,7 @@ fn quote_arrow_deserializer( }} } - _ => unimplemented!("{datatype:#?}"), // NOLINT + _ => unimplemented!("{datatype:#?}"), } } } @@ -1959,7 +1963,7 @@ fn quote_arrow_field_deserializer( ); let inner_obj = if let DataType::Extension(fqname, _, _) = datatype { - Some(objects.get(fqname)) + Some(&objects[fqname]) } else { None }; @@ -2104,7 +2108,7 @@ fn quote_arrow_field_deserializer( quote!(#fqname_use::try_from_arrow_opt(#data_src)?.into_iter()) } - _ => unimplemented!("{datatype:#?}"), // NOLINT + _ => unimplemented!("{datatype:#?}"), } } diff --git a/crates/re_types_builder/src/lib.rs b/crates/re_types_builder/src/lib.rs index 54077c3c107a..90c2b180e990 100644 --- a/crates/re_types_builder/src/lib.rs +++ b/crates/re_types_builder/src/lib.rs @@ -371,3 +371,30 @@ pub(crate) fn rerun_workspace_path() -> camino::Utf8PathBuf { workspace_root } + +pub(crate) fn to_snake_case(s: &str) -> String { + // Other crates (convert_case, case, heck, …) all get this wrong. See unit test. + let mut last_char: Option = None; + + let mut out = String::new(); + for c in s.chars() { + if let Some(last_char) = last_char { + if last_char.is_lowercase() && c.is_uppercase() { + out.push('_'); + } + } + out.push(c.to_ascii_lowercase()); + last_char = Some(c); + } + + out +} + +#[test] +fn test_snake_case() { + assert_eq!(to_snake_case("Point2D"), "point2d"); + assert_eq!( + to_snake_case("TranslationAndMat3x3"), + "translation_and_mat3x3" + ); +} diff --git a/crates/re_types_builder/src/objects.rs b/crates/re_types_builder/src/objects.rs index 01f478521143..66c512580544 100644 --- a/crates/re_types_builder/src/objects.rs +++ b/crates/re_types_builder/src/objects.rs @@ -148,25 +148,6 @@ impl Objects { } impl Objects { - /// Returns a resolved object using its fully-qualified name. - /// - /// Panics if missing. - /// - /// E.g.: - /// ```ignore - /// resolved.get("rerun.datatypes.Vec3D"); - /// resolved.get("rerun.datatypes.Angle"); - /// resolved.get("rerun.components.Label"); - /// resolved.get("rerun.archetypes.Point2D"); - /// ``` - pub fn get(&self, fqname: impl AsRef) -> &Object { - let fqname = fqname.as_ref(); - self.objects - .get(fqname) - .with_context(|| format!("unknown object: {fqname:?}")) - .unwrap() - } - /// Returns all available objects, pre-sorted in ascending order based on their `order` /// attribute. pub fn ordered_objects_mut(&mut self, kind: Option) -> Vec<&mut Object> { @@ -196,6 +177,28 @@ impl Objects { } } +/// Returns a resolved object using its fully-qualified name. +/// +/// Panics if missing. +/// +/// E.g.: +/// ```ignore +/// # let objects = Objects::default(); +/// let obj = &objects["rerun.datatypes.Vec3D"]; +/// let obj = &objects["rerun.datatypes.Angle"]; +/// let obj = &objects["rerun.components.Label"]; +/// let obj = &objects["rerun.archetypes.Point2D"]; +/// ``` +impl std::ops::Index<&str> for Objects { + type Output = Object; + + fn index(&self, fqname: &str) -> &Self::Output { + self.objects + .get(fqname) + .unwrap_or_else(|| panic!("unknown object: {fqname:?}")) + } +} + // --- /// The kind of the object, as determined by its package root (e.g. `rerun.components`). @@ -607,6 +610,13 @@ impl Object { .is_some() } + /// Is the destructor trivial/default (i.e. is this simple data with no allocations)? + pub fn has_default_destructor(&self, objects: &Objects) -> bool { + self.fields + .iter() + .all(|field| field.typ.has_default_destructor(objects)) + } + /// Try to find the relative file path of the `.fbs` source file. pub fn relative_filepath(&self) -> Option<&Utf8Path> { self.filepath @@ -616,41 +626,14 @@ impl Object { /// The `snake_case` name of the object, e.g. `translation_and_mat3x3`. pub fn snake_case_name(&self) -> String { - to_snake_case(&self.name) - } -} - -fn to_snake_case(s: &str) -> String { - // Other crates (convert_case, case, heck, …) all get this wrong. See unit test. - let mut last_char: Option = None; - - let mut out = String::new(); - for c in s.chars() { - if let Some(last_char) = last_char { - if last_char.is_lowercase() && c.is_uppercase() { - out.push('_'); - } - } - out.push(c.to_ascii_lowercase()); - last_char = Some(c); + crate::to_snake_case(&self.name) } - - out -} - -#[test] -fn test_snake_case() { - assert_eq!(to_snake_case("Point2D"), "point2d"); - assert_eq!( - to_snake_case("TranslationAndMat3x3"), - "translation_and_mat3x3" - ); } /// Properties specific to either structs or unions, but not both. #[derive(Debug, Clone)] pub enum ObjectSpecifics { - Struct {}, + Struct, Union { /// The underlying type of the union. /// @@ -834,7 +817,7 @@ impl ObjectField { } /// The underlying type of an [`ObjectField`]. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum Type { UInt8, UInt16, @@ -927,8 +910,8 @@ impl Type { ), }, FbsBaseType::None | FbsBaseType::UType | FbsBaseType::Vector64 => { - unimplemented!("{typ:#?}") // NOLINT - } // NOLINT + unimplemented!("{typ:#?}") + } // NOTE: `FbsBaseType` isn't actually an enum, it's just a bunch of constants... _ => unreachable!("{typ:#?}"), } @@ -937,47 +920,71 @@ impl Type { /// True if this is some kind of array/vector. pub fn is_plural(&self) -> bool { match self { - Type::Array { + Self::Array { elem_type: _, length: _, } - | Type::Vector { elem_type: _ } => true, - Type::UInt8 - | Type::UInt16 - | Type::UInt32 - | Type::UInt64 - | Type::Int8 - | Type::Int16 - | Type::Int32 - | Type::Int64 - | Type::Bool - | Type::Float16 - | Type::Float32 - | Type::Float64 - | Type::String - | Type::Object(_) => false, + | Self::Vector { elem_type: _ } => true, + Self::UInt8 + | Self::UInt16 + | Self::UInt32 + | Self::UInt64 + | Self::Int8 + | Self::Int16 + | Self::Int32 + | Self::Int64 + | Self::Bool + | Self::Float16 + | Self::Float32 + | Self::Float64 + | Self::String + | Self::Object(_) => false, } } /// `Some(fqname)` if this is an `Object` or an `Array`/`Vector` of `Object`s. pub fn fqname(&self) -> Option<&str> { match self { - Type::Object(fqname) => Some(fqname.as_str()), - Type::Array { + Self::Object(fqname) => Some(fqname.as_str()), + Self::Array { elem_type, length: _, } - | Type::Vector { elem_type } => elem_type.fqname(), + | Self::Vector { elem_type } => elem_type.fqname(), _ => None, } } + + /// Is the destructor trivial/default (i.e. is this simple data with no allocations)? + pub fn has_default_destructor(&self, objects: &Objects) -> bool { + match self { + Self::UInt8 + | Self::UInt16 + | Self::UInt32 + | Self::UInt64 + | Self::Int8 + | Self::Int16 + | Self::Int32 + | Self::Int64 + | Self::Bool + | Self::Float16 + | Self::Float32 + | Self::Float64 => true, + + Self::String | Self::Vector { .. } => false, + + Self::Array { elem_type, .. } => elem_type.has_default_destructor(objects), + + Self::Object(fqname) => objects[fqname].has_default_destructor(objects), + } + } } /// The underlying element type for arrays/vectors/maps. /// /// Flatbuffers doesn't support directly nesting multiple layers of arrays, they /// always have to be wrapped into intermediate layers of structs or tables! -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum ElementType { UInt8, UInt16, @@ -1037,10 +1044,32 @@ impl ElementType { /// `Some(fqname)` if this is an `Object`. pub fn fqname(&self) -> Option<&str> { match self { - ElementType::Object(fqname) => Some(fqname.as_str()), + Self::Object(fqname) => Some(fqname.as_str()), _ => None, } } + + /// Is the destructor trivial/default (i.e. is this simple data with no allocations)? + pub fn has_default_destructor(&self, objects: &Objects) -> bool { + match self { + Self::UInt8 + | Self::UInt16 + | Self::UInt32 + | Self::UInt64 + | Self::Int8 + | Self::Int16 + | Self::Int32 + | Self::Int64 + | Self::Bool + | Self::Float16 + | Self::Float32 + | Self::Float64 => true, + + Self::String => false, + + Self::Object(fqname) => objects[fqname].has_default_destructor(objects), + } + } } // --- Common --- diff --git a/examples/cpp/minimal/main.cpp b/examples/cpp/minimal/main.cpp index 232fb5346720..dd8e246b152a 100644 --- a/examples/cpp/minimal/main.cpp +++ b/examples/cpp/minimal/main.cpp @@ -22,4 +22,9 @@ int main(int argc, char** argv) { uint32_t num_instances = 3; rr_stream.log_data_row("points", num_instances, 1, data_cells); + + // Test some type instantiation + auto tls = rr::datatypes::TranslationRotationScale3D{}; + tls.translation = {1.0, 2.0, 3.0}; + rr::datatypes::Transform3D t = std::move(tls); } diff --git a/rerun_cpp/src/archetypes/affix_fuzzer1.hpp b/rerun_cpp/src/archetypes/affix_fuzzer1.hpp index b0eb55938c30..4de99588bbc9 100644 --- a/rerun_cpp/src/archetypes/affix_fuzzer1.hpp +++ b/rerun_cpp/src/archetypes/affix_fuzzer1.hpp @@ -3,8 +3,169 @@ #pragma once +#include +#include +#include + +#include "../components/affix_fuzzer1.hpp" +#include "../components/affix_fuzzer10.hpp" +#include "../components/affix_fuzzer11.hpp" +#include "../components/affix_fuzzer12.hpp" +#include "../components/affix_fuzzer13.hpp" +#include "../components/affix_fuzzer14.hpp" +#include "../components/affix_fuzzer16.hpp" +#include "../components/affix_fuzzer17.hpp" +#include "../components/affix_fuzzer18.hpp" +#include "../components/affix_fuzzer19.hpp" +#include "../components/affix_fuzzer2.hpp" +#include "../components/affix_fuzzer3.hpp" +#include "../components/affix_fuzzer4.hpp" +#include "../components/affix_fuzzer5.hpp" +#include "../components/affix_fuzzer6.hpp" +#include "../components/affix_fuzzer7.hpp" +#include "../components/affix_fuzzer8.hpp" +#include "../components/affix_fuzzer9.hpp" + namespace rr { namespace archetypes { - struct AffixFuzzer1 {}; + struct AffixFuzzer1 { + rr::components::AffixFuzzer1 fuzz1001; + + rr::components::AffixFuzzer2 fuzz1002; + + rr::components::AffixFuzzer3 fuzz1003; + + rr::components::AffixFuzzer4 fuzz1004; + + rr::components::AffixFuzzer5 fuzz1005; + + rr::components::AffixFuzzer6 fuzz1006; + + rr::components::AffixFuzzer7 fuzz1007; + + rr::components::AffixFuzzer8 fuzz1008; + + rr::components::AffixFuzzer9 fuzz1009; + + rr::components::AffixFuzzer10 fuzz1010; + + rr::components::AffixFuzzer11 fuzz1011; + + rr::components::AffixFuzzer12 fuzz1012; + + rr::components::AffixFuzzer13 fuzz1013; + + rr::components::AffixFuzzer14 fuzz1014; + + rr::components::AffixFuzzer16 fuzz1016; + + rr::components::AffixFuzzer17 fuzz1017; + + rr::components::AffixFuzzer18 fuzz1018; + + rr::components::AffixFuzzer19 fuzz1019; + + std::vector fuzz1101; + + std::vector fuzz1102; + + std::vector fuzz1103; + + std::vector fuzz1104; + + std::vector fuzz1105; + + std::vector fuzz1106; + + std::vector fuzz1107; + + std::vector fuzz1108; + + std::vector fuzz1109; + + std::vector fuzz1110; + + std::vector fuzz1111; + + std::vector fuzz1112; + + std::vector fuzz1113; + + std::vector fuzz1114; + + std::vector fuzz1116; + + std::vector fuzz1117; + + std::vector fuzz1118; + + std::optional fuzz2001; + + std::optional fuzz2002; + + std::optional fuzz2003; + + std::optional fuzz2004; + + std::optional fuzz2005; + + std::optional fuzz2006; + + std::optional fuzz2007; + + std::optional fuzz2008; + + std::optional fuzz2009; + + std::optional fuzz2010; + + std::optional fuzz2011; + + std::optional fuzz2012; + + std::optional fuzz2013; + + std::optional fuzz2014; + + std::optional fuzz2016; + + std::optional fuzz2017; + + std::optional fuzz2018; + + std::optional> fuzz2101; + + std::optional> fuzz2102; + + std::optional> fuzz2103; + + std::optional> fuzz2104; + + std::optional> fuzz2105; + + std::optional> fuzz2106; + + std::optional> fuzz2107; + + std::optional> fuzz2108; + + std::optional> fuzz2109; + + std::optional> fuzz2110; + + std::optional> fuzz2111; + + std::optional> fuzz2112; + + std::optional> fuzz2113; + + std::optional> fuzz2114; + + std::optional> fuzz2116; + + std::optional> fuzz2117; + + std::optional> fuzz2118; + }; } // namespace archetypes } // namespace rr diff --git a/rerun_cpp/src/archetypes/points2d.hpp b/rerun_cpp/src/archetypes/points2d.hpp index 55dcc49fca5b..b26fefc05526 100644 --- a/rerun_cpp/src/archetypes/points2d.hpp +++ b/rerun_cpp/src/archetypes/points2d.hpp @@ -3,8 +3,57 @@ #pragma once +#include +#include +#include + +#include "../components/class_id.hpp" +#include "../components/color.hpp" +#include "../components/draw_order.hpp" +#include "../components/instance_key.hpp" +#include "../components/keypoint_id.hpp" +#include "../components/label.hpp" +#include "../components/point2d.hpp" +#include "../components/radius.hpp" + namespace rr { namespace archetypes { - struct Points2D {}; + /// A 2D point cloud with positions and optional colors, radii, labels, etc. + struct Points2D { + /// All the actual 2D points that make up the point cloud. + std::vector points; + + /// Optional radii for the points, effectively turning them into circles. + std::optional> radii; + + /// Optional colors for the points. + std::optional> colors; + + /// Optional text labels for the points. + std::optional> labels; + + /// An optional floating point value that specifies the 2D drawing order. + /// Objects with higher values are drawn on top of those with lower values. + /// + /// The default for 2D points is 30.0. + std::optional draw_order; + + /// Optional class Ids for the points. + /// + /// The class ID provides colors and labels if not specified explicitly. + std::optional> class_ids; + + /// Optional keypoint IDs for the points, identifying them within a class. + /// + /// If keypoint IDs are passed in but no class IDs were specified, the class ID will + /// default to 0. + /// This is useful to identify points within a single classification (which is + /// identified with `class_id`). E.g. the classification might be 'Person' and the + /// keypoints refer to joints on a detected skeleton. + std::optional> keypoint_ids; + + /// Unique identifiers for each individual point in the batch. + std::optional> instance_keys; + }; } // namespace archetypes } // namespace rr diff --git a/rerun_cpp/src/archetypes/transform3d.hpp b/rerun_cpp/src/archetypes/transform3d.hpp index 9c416dfc1f83..0172414db041 100644 --- a/rerun_cpp/src/archetypes/transform3d.hpp +++ b/rerun_cpp/src/archetypes/transform3d.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include + +#include "../components/transform3d.hpp" + namespace rr { namespace archetypes { - struct Transform3D {}; + /// A 3D transform + struct Transform3D { + /// The transform + rr::components::Transform3D transform; + + Transform3D(rr::components::Transform3D transform) : transform(std::move(transform)) {} + }; } // namespace archetypes } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer1.hpp b/rerun_cpp/src/components/affix_fuzzer1.hpp index 9cc9fd3f604d..85b25be495c7 100644 --- a/rerun_cpp/src/components/affix_fuzzer1.hpp +++ b/rerun_cpp/src/components/affix_fuzzer1.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace components { - struct AffixFuzzer1 {}; + struct AffixFuzzer1 { + rr::datatypes::AffixFuzzer1 single_required; + + AffixFuzzer1(rr::datatypes::AffixFuzzer1 single_required) + : single_required(std::move(single_required)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer10.hpp b/rerun_cpp/src/components/affix_fuzzer10.hpp index 5b1816e8d812..65c2ccf443ee 100644 --- a/rerun_cpp/src/components/affix_fuzzer10.hpp +++ b/rerun_cpp/src/components/affix_fuzzer10.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include +#include +#include + namespace rr { namespace components { - struct AffixFuzzer10 {}; + struct AffixFuzzer10 { + std::optional single_string_optional; + + AffixFuzzer10(std::optional single_string_optional) + : single_string_optional(std::move(single_string_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer11.hpp b/rerun_cpp/src/components/affix_fuzzer11.hpp index 9e3f238aa543..18203fdf4cb0 100644 --- a/rerun_cpp/src/components/affix_fuzzer11.hpp +++ b/rerun_cpp/src/components/affix_fuzzer11.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include +#include +#include + namespace rr { namespace components { - struct AffixFuzzer11 {}; + struct AffixFuzzer11 { + std::optional> many_floats_optional; + + AffixFuzzer11(std::optional> many_floats_optional) + : many_floats_optional(std::move(many_floats_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer12.hpp b/rerun_cpp/src/components/affix_fuzzer12.hpp index dc10de4c165a..04c1d5086720 100644 --- a/rerun_cpp/src/components/affix_fuzzer12.hpp +++ b/rerun_cpp/src/components/affix_fuzzer12.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include +#include +#include + namespace rr { namespace components { - struct AffixFuzzer12 {}; + struct AffixFuzzer12 { + std::vector many_strings_required; + + AffixFuzzer12(std::vector many_strings_required) + : many_strings_required(std::move(many_strings_required)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer13.hpp b/rerun_cpp/src/components/affix_fuzzer13.hpp index acf215e418b6..6c46213cf8a4 100644 --- a/rerun_cpp/src/components/affix_fuzzer13.hpp +++ b/rerun_cpp/src/components/affix_fuzzer13.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include +#include +#include +#include + namespace rr { namespace components { - struct AffixFuzzer13 {}; + struct AffixFuzzer13 { + std::optional> many_strings_optional; + + AffixFuzzer13(std::optional> many_strings_optional) + : many_strings_optional(std::move(many_strings_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer14.hpp b/rerun_cpp/src/components/affix_fuzzer14.hpp index 85c76208fae9..95e7b960581d 100644 --- a/rerun_cpp/src/components/affix_fuzzer14.hpp +++ b/rerun_cpp/src/components/affix_fuzzer14.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include + +#include "../datatypes/affix_fuzzer3.hpp" + namespace rr { namespace components { - struct AffixFuzzer14 {}; + struct AffixFuzzer14 { + rr::datatypes::AffixFuzzer3 single_required_union; + + AffixFuzzer14(rr::datatypes::AffixFuzzer3 single_required_union) + : single_required_union(std::move(single_required_union)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer16.hpp b/rerun_cpp/src/components/affix_fuzzer16.hpp index dd87e84d8aef..f35f1b6c5cec 100644 --- a/rerun_cpp/src/components/affix_fuzzer16.hpp +++ b/rerun_cpp/src/components/affix_fuzzer16.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include +#include + +#include "../datatypes/affix_fuzzer3.hpp" + namespace rr { namespace components { - struct AffixFuzzer16 {}; + struct AffixFuzzer16 { + std::vector many_required_unions; + + AffixFuzzer16(std::vector many_required_unions) + : many_required_unions(std::move(many_required_unions)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer17.hpp b/rerun_cpp/src/components/affix_fuzzer17.hpp index fd9705d106f4..0e8a24f02958 100644 --- a/rerun_cpp/src/components/affix_fuzzer17.hpp +++ b/rerun_cpp/src/components/affix_fuzzer17.hpp @@ -3,8 +3,21 @@ #pragma once +#include +#include +#include +#include + +#include "../datatypes/affix_fuzzer3.hpp" + namespace rr { namespace components { - struct AffixFuzzer17 {}; + struct AffixFuzzer17 { + std::optional> many_optional_unions; + + AffixFuzzer17( + std::optional> many_optional_unions) + : many_optional_unions(std::move(many_optional_unions)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer18.hpp b/rerun_cpp/src/components/affix_fuzzer18.hpp index 70a1fd8074fd..f11c433aac72 100644 --- a/rerun_cpp/src/components/affix_fuzzer18.hpp +++ b/rerun_cpp/src/components/affix_fuzzer18.hpp @@ -3,8 +3,21 @@ #pragma once +#include +#include +#include +#include + +#include "../datatypes/affix_fuzzer4.hpp" + namespace rr { namespace components { - struct AffixFuzzer18 {}; + struct AffixFuzzer18 { + std::optional> many_optional_unions; + + AffixFuzzer18( + std::optional> many_optional_unions) + : many_optional_unions(std::move(many_optional_unions)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer19.hpp b/rerun_cpp/src/components/affix_fuzzer19.hpp index a743b9bb26c6..732f2abe9494 100644 --- a/rerun_cpp/src/components/affix_fuzzer19.hpp +++ b/rerun_cpp/src/components/affix_fuzzer19.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include + +#include "../datatypes/affix_fuzzer5.hpp" + namespace rr { namespace components { - struct AffixFuzzer19 {}; + struct AffixFuzzer19 { + rr::datatypes::AffixFuzzer5 just_a_table_nothing_shady; + + AffixFuzzer19(rr::datatypes::AffixFuzzer5 just_a_table_nothing_shady) + : just_a_table_nothing_shady(std::move(just_a_table_nothing_shady)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer2.hpp b/rerun_cpp/src/components/affix_fuzzer2.hpp index 9823feee90d6..fb321482adae 100644 --- a/rerun_cpp/src/components/affix_fuzzer2.hpp +++ b/rerun_cpp/src/components/affix_fuzzer2.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace components { - struct AffixFuzzer2 {}; + struct AffixFuzzer2 { + rr::datatypes::AffixFuzzer1 single_required; + + AffixFuzzer2(rr::datatypes::AffixFuzzer1 single_required) + : single_required(std::move(single_required)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer3.hpp b/rerun_cpp/src/components/affix_fuzzer3.hpp index b873cf127dda..9f11bccccada 100644 --- a/rerun_cpp/src/components/affix_fuzzer3.hpp +++ b/rerun_cpp/src/components/affix_fuzzer3.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace components { - struct AffixFuzzer3 {}; + struct AffixFuzzer3 { + rr::datatypes::AffixFuzzer1 single_required; + + AffixFuzzer3(rr::datatypes::AffixFuzzer1 single_required) + : single_required(std::move(single_required)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer4.hpp b/rerun_cpp/src/components/affix_fuzzer4.hpp index a8647af8d7c5..0ec512f72ca9 100644 --- a/rerun_cpp/src/components/affix_fuzzer4.hpp +++ b/rerun_cpp/src/components/affix_fuzzer4.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace components { - struct AffixFuzzer4 {}; + struct AffixFuzzer4 { + std::optional single_optional; + + AffixFuzzer4(std::optional single_optional) + : single_optional(std::move(single_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer5.hpp b/rerun_cpp/src/components/affix_fuzzer5.hpp index b8f429202e99..72c4c1e91397 100644 --- a/rerun_cpp/src/components/affix_fuzzer5.hpp +++ b/rerun_cpp/src/components/affix_fuzzer5.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace components { - struct AffixFuzzer5 {}; + struct AffixFuzzer5 { + std::optional single_optional; + + AffixFuzzer5(std::optional single_optional) + : single_optional(std::move(single_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer6.hpp b/rerun_cpp/src/components/affix_fuzzer6.hpp index 89a9632ab43a..ec647c89c153 100644 --- a/rerun_cpp/src/components/affix_fuzzer6.hpp +++ b/rerun_cpp/src/components/affix_fuzzer6.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace components { - struct AffixFuzzer6 {}; + struct AffixFuzzer6 { + std::optional single_optional; + + AffixFuzzer6(std::optional single_optional) + : single_optional(std::move(single_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer7.hpp b/rerun_cpp/src/components/affix_fuzzer7.hpp index b18525552184..ee6d95714846 100644 --- a/rerun_cpp/src/components/affix_fuzzer7.hpp +++ b/rerun_cpp/src/components/affix_fuzzer7.hpp @@ -3,8 +3,20 @@ #pragma once +#include +#include +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace components { - struct AffixFuzzer7 {}; + struct AffixFuzzer7 { + std::optional> many_optional; + + AffixFuzzer7(std::optional> many_optional) + : many_optional(std::move(many_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer8.hpp b/rerun_cpp/src/components/affix_fuzzer8.hpp index 804b3fdab5a9..83aaa366f774 100644 --- a/rerun_cpp/src/components/affix_fuzzer8.hpp +++ b/rerun_cpp/src/components/affix_fuzzer8.hpp @@ -3,8 +3,17 @@ #pragma once +#include +#include +#include + namespace rr { namespace components { - struct AffixFuzzer8 {}; + struct AffixFuzzer8 { + std::optional single_float_optional; + + AffixFuzzer8(std::optional single_float_optional) + : single_float_optional(std::move(single_float_optional)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/affix_fuzzer9.hpp b/rerun_cpp/src/components/affix_fuzzer9.hpp index ee69699cf55a..2358a6194126 100644 --- a/rerun_cpp/src/components/affix_fuzzer9.hpp +++ b/rerun_cpp/src/components/affix_fuzzer9.hpp @@ -3,8 +3,17 @@ #pragma once +#include +#include +#include + namespace rr { namespace components { - struct AffixFuzzer9 {}; + struct AffixFuzzer9 { + std::string single_string_required; + + AffixFuzzer9(std::string single_string_required) + : single_string_required(std::move(single_string_required)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/class_id.hpp b/rerun_cpp/src/components/class_id.hpp index f6ef8c401a00..b3d6c0331f4c 100644 --- a/rerun_cpp/src/components/class_id.hpp +++ b/rerun_cpp/src/components/class_id.hpp @@ -3,8 +3,16 @@ #pragma once +#include +#include + namespace rr { namespace components { - struct ClassId {}; + /// A 16-bit ID representing a type of semantic class. + struct ClassId { + uint16_t id; + + ClassId(uint16_t id) : id(std::move(id)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/color.hpp b/rerun_cpp/src/components/color.hpp index d3d6ad7e00c4..e2c7c0dbd11f 100644 --- a/rerun_cpp/src/components/color.hpp +++ b/rerun_cpp/src/components/color.hpp @@ -3,8 +3,17 @@ #pragma once +#include +#include + namespace rr { namespace components { - struct Color {}; + /// An RGBA color tuple with unmultiplied/separate alpha, in sRGB gamma space with linear + /// alpha. + struct Color { + uint32_t rgba; + + Color(uint32_t rgba) : rgba(std::move(rgba)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/draw_order.hpp b/rerun_cpp/src/components/draw_order.hpp index fa44ea4c8e5f..e50caa232be4 100644 --- a/rerun_cpp/src/components/draw_order.hpp +++ b/rerun_cpp/src/components/draw_order.hpp @@ -3,8 +3,22 @@ #pragma once +#include +#include + namespace rr { namespace components { - struct DrawOrder {}; + /// Draw order used for the display order of 2D elements. + /// + /// Higher values are drawn on top of lower values. + /// An entity can have only a single draw order component. + /// Within an entity draw order is governed by the order of the components. + /// + /// Draw order for entities with the same draw order is generally undefined. + struct DrawOrder { + float value; + + DrawOrder(float value) : value(std::move(value)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/instance_key.hpp b/rerun_cpp/src/components/instance_key.hpp index 23bd147e8f9c..64e319b303d5 100644 --- a/rerun_cpp/src/components/instance_key.hpp +++ b/rerun_cpp/src/components/instance_key.hpp @@ -3,8 +3,16 @@ #pragma once +#include +#include + namespace rr { namespace components { - struct InstanceKey {}; + /// A unique numeric identifier for each individual instance within a batch. + struct InstanceKey { + uint64_t value; + + InstanceKey(uint64_t value) : value(std::move(value)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/keypoint_id.hpp b/rerun_cpp/src/components/keypoint_id.hpp index 1535d55752f4..1194ea12f982 100644 --- a/rerun_cpp/src/components/keypoint_id.hpp +++ b/rerun_cpp/src/components/keypoint_id.hpp @@ -3,8 +3,16 @@ #pragma once +#include +#include + namespace rr { namespace components { - struct KeypointId {}; + /// A 16-bit ID representing a type of semantic keypoint within a class. + struct KeypointId { + uint16_t id; + + KeypointId(uint16_t id) : id(std::move(id)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/label.hpp b/rerun_cpp/src/components/label.hpp index 69aa9a091357..00e5bdaab673 100644 --- a/rerun_cpp/src/components/label.hpp +++ b/rerun_cpp/src/components/label.hpp @@ -3,8 +3,17 @@ #pragma once +#include +#include +#include + namespace rr { namespace components { - struct Label {}; + /// A String label component. + struct Label { + std::string value; + + Label(std::string value) : value(std::move(value)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/point2d.hpp b/rerun_cpp/src/components/point2d.hpp index 7204ea78c973..ce28f099ea73 100644 --- a/rerun_cpp/src/components/point2d.hpp +++ b/rerun_cpp/src/components/point2d.hpp @@ -3,8 +3,18 @@ #pragma once +#include +#include + +#include "../datatypes/point2d.hpp" + namespace rr { namespace components { - struct Point2D {}; + /// A point in 2D space. + struct Point2D { + rr::datatypes::Point2D xy; + + Point2D(rr::datatypes::Point2D xy) : xy(std::move(xy)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/radius.hpp b/rerun_cpp/src/components/radius.hpp index 1ad045b4259f..006bf732dbdd 100644 --- a/rerun_cpp/src/components/radius.hpp +++ b/rerun_cpp/src/components/radius.hpp @@ -3,8 +3,16 @@ #pragma once +#include +#include + namespace rr { namespace components { - struct Radius {}; + /// A Radius component. + struct Radius { + float value; + + Radius(float value) : value(std::move(value)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/components/transform3d.hpp b/rerun_cpp/src/components/transform3d.hpp index 9835ca613ecd..bc1f68752a62 100644 --- a/rerun_cpp/src/components/transform3d.hpp +++ b/rerun_cpp/src/components/transform3d.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include + +#include "../datatypes/transform3d.hpp" + namespace rr { namespace components { - struct Transform3D {}; + /// An affine transform between two 3D spaces, represented in a given direction. + struct Transform3D { + /// Representation of the transform. + rr::datatypes::Transform3D repr; + + Transform3D(rr::datatypes::Transform3D repr) : repr(std::move(repr)) {} + }; } // namespace components } // namespace rr diff --git a/rerun_cpp/src/datatypes/affix_fuzzer1.hpp b/rerun_cpp/src/datatypes/affix_fuzzer1.hpp index 9a04471f8c2e..cb10bdd3e067 100644 --- a/rerun_cpp/src/datatypes/affix_fuzzer1.hpp +++ b/rerun_cpp/src/datatypes/affix_fuzzer1.hpp @@ -3,8 +3,33 @@ #pragma once +#include +#include +#include +#include + +#include "../datatypes/flattened_scalar.hpp" + namespace rr { namespace datatypes { - struct AffixFuzzer1 {}; + struct AffixFuzzer1 { + std::optional single_float_optional; + + std::string single_string_required; + + std::optional single_string_optional; + + std::optional> many_floats_optional; + + std::vector many_strings_required; + + std::optional> many_strings_optional; + + float flattened_scalar; + + rr::datatypes::FlattenedScalar almost_flattened_scalar; + + std::optional from_parent; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/affix_fuzzer2.hpp b/rerun_cpp/src/datatypes/affix_fuzzer2.hpp index 720f026a5d2a..d74eeb296f5b 100644 --- a/rerun_cpp/src/datatypes/affix_fuzzer2.hpp +++ b/rerun_cpp/src/datatypes/affix_fuzzer2.hpp @@ -3,8 +3,17 @@ #pragma once +#include +#include +#include + namespace rr { namespace datatypes { - struct AffixFuzzer2 {}; + struct AffixFuzzer2 { + std::optional single_float_optional; + + AffixFuzzer2(std::optional single_float_optional) + : single_float_optional(std::move(single_float_optional)) {} + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/affix_fuzzer3.hpp b/rerun_cpp/src/datatypes/affix_fuzzer3.hpp index 2481cc71b51f..30eec0a0d507 100644 --- a/rerun_cpp/src/datatypes/affix_fuzzer3.hpp +++ b/rerun_cpp/src/datatypes/affix_fuzzer3.hpp @@ -3,8 +3,130 @@ #pragma once +#include +#include +#include +#include +#include +#include + +#include "../datatypes/affix_fuzzer1.hpp" + namespace rr { namespace datatypes { - struct AffixFuzzer3 {}; + namespace detail { + enum class AffixFuzzer3Tag { + /// Having a special empty state makes it possible to implement move-semantics. We + /// need to be able to leave the object in a state which we can run the destructor + /// on. + NONE = 0, + degrees, + radians, + craziness, + fixed_size_shenanigans, + }; + + union AffixFuzzer3Data { + float degrees; + + std::optional radians; + + std::vector craziness; + + float fixed_size_shenanigans[3]; + + AffixFuzzer3Data() {} + + ~AffixFuzzer3Data() {} + + void swap(AffixFuzzer3Data& other) noexcept { + // This bitwise swap would fail for self-referential types, but we don't have + // any of those. + char temp[sizeof(AffixFuzzer3Data)]; + std::memcpy(temp, this, sizeof(AffixFuzzer3Data)); + std::memcpy(this, &other, sizeof(AffixFuzzer3Data)); + std::memcpy(&other, temp, sizeof(AffixFuzzer3Data)); + } + }; + } // namespace detail + + struct AffixFuzzer3 { + private: + detail::AffixFuzzer3Tag _tag; + detail::AffixFuzzer3Data _data; + + AffixFuzzer3() : _tag(detail::AffixFuzzer3Tag::NONE) {} + + public: + AffixFuzzer3(AffixFuzzer3&& other) noexcept : _tag(detail::AffixFuzzer3Tag::NONE) { + this->swap(other); + } + + AffixFuzzer3& operator=(AffixFuzzer3&& other) noexcept { + this->swap(other); + return *this; + } + + ~AffixFuzzer3() { + switch (this->_tag) { + case detail::AffixFuzzer3Tag::NONE: { + break; // Nothing to destroy + } + case detail::AffixFuzzer3Tag::degrees: { + break; // has a trivial destructor + } + case detail::AffixFuzzer3Tag::radians: { + break; // has a trivial destructor + } + case detail::AffixFuzzer3Tag::craziness: { + typedef std::vector TypeAlias; + _data.craziness.~TypeAlias(); + break; + } + case detail::AffixFuzzer3Tag::fixed_size_shenanigans: { + break; // has a trivial destructor + } + } + } + + static AffixFuzzer3 degrees(float degrees) { + AffixFuzzer3 self; + self._tag = detail::AffixFuzzer3Tag::degrees; + self._data.degrees = std::move(degrees); + return std::move(self); + } + + static AffixFuzzer3 radians(std::optional radians) { + AffixFuzzer3 self; + self._tag = detail::AffixFuzzer3Tag::radians; + self._data.radians = std::move(radians); + return std::move(self); + } + + static AffixFuzzer3 craziness(std::vector craziness) { + typedef std::vector TypeAlias; + AffixFuzzer3 self; + self._tag = detail::AffixFuzzer3Tag::craziness; + new (&self._data.craziness) TypeAlias(std::move(craziness)); + return std::move(self); + } + + static AffixFuzzer3 fixed_size_shenanigans(float fixed_size_shenanigans[3]) { + typedef float TypeAlias; + AffixFuzzer3 self; + self._tag = detail::AffixFuzzer3Tag::fixed_size_shenanigans; + for (size_t i = 0; i < 3; i += 1) { + self._data.fixed_size_shenanigans[i] = std::move(fixed_size_shenanigans[i]); + } + return std::move(self); + } + + void swap(AffixFuzzer3& other) noexcept { + auto tag_temp = this->_tag; + this->_tag = other._tag; + other._tag = tag_temp; + this->_data.swap(other._data); + } + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/affix_fuzzer4.hpp b/rerun_cpp/src/datatypes/affix_fuzzer4.hpp index 719febb75f44..7e5a4f03133d 100644 --- a/rerun_cpp/src/datatypes/affix_fuzzer4.hpp +++ b/rerun_cpp/src/datatypes/affix_fuzzer4.hpp @@ -3,8 +3,122 @@ #pragma once +#include +#include +#include +#include +#include +#include + +#include "../datatypes/affix_fuzzer3.hpp" + namespace rr { namespace datatypes { - struct AffixFuzzer4 {}; + namespace detail { + enum class AffixFuzzer4Tag { + /// Having a special empty state makes it possible to implement move-semantics. We + /// need to be able to leave the object in a state which we can run the destructor + /// on. + NONE = 0, + single_required, + many_required, + many_optional, + }; + + union AffixFuzzer4Data { + rr::datatypes::AffixFuzzer3 single_required; + + std::vector many_required; + + std::optional> many_optional; + + AffixFuzzer4Data() {} + + ~AffixFuzzer4Data() {} + + void swap(AffixFuzzer4Data& other) noexcept { + // This bitwise swap would fail for self-referential types, but we don't have + // any of those. + char temp[sizeof(AffixFuzzer4Data)]; + std::memcpy(temp, this, sizeof(AffixFuzzer4Data)); + std::memcpy(this, &other, sizeof(AffixFuzzer4Data)); + std::memcpy(&other, temp, sizeof(AffixFuzzer4Data)); + } + }; + } // namespace detail + + struct AffixFuzzer4 { + private: + detail::AffixFuzzer4Tag _tag; + detail::AffixFuzzer4Data _data; + + AffixFuzzer4() : _tag(detail::AffixFuzzer4Tag::NONE) {} + + public: + AffixFuzzer4(AffixFuzzer4&& other) noexcept : _tag(detail::AffixFuzzer4Tag::NONE) { + this->swap(other); + } + + AffixFuzzer4& operator=(AffixFuzzer4&& other) noexcept { + this->swap(other); + return *this; + } + + ~AffixFuzzer4() { + switch (this->_tag) { + case detail::AffixFuzzer4Tag::NONE: { + break; // Nothing to destroy + } + case detail::AffixFuzzer4Tag::single_required: { + typedef rr::datatypes::AffixFuzzer3 TypeAlias; + _data.single_required.~TypeAlias(); + break; + } + case detail::AffixFuzzer4Tag::many_required: { + typedef std::vector TypeAlias; + _data.many_required.~TypeAlias(); + break; + } + case detail::AffixFuzzer4Tag::many_optional: { + typedef std::optional> TypeAlias; + _data.many_optional.~TypeAlias(); + break; + } + } + } + + static AffixFuzzer4 single_required(rr::datatypes::AffixFuzzer3 single_required) { + typedef rr::datatypes::AffixFuzzer3 TypeAlias; + AffixFuzzer4 self; + self._tag = detail::AffixFuzzer4Tag::single_required; + new (&self._data.single_required) TypeAlias(std::move(single_required)); + return std::move(self); + } + + static AffixFuzzer4 many_required( + std::vector many_required) { + typedef std::vector TypeAlias; + AffixFuzzer4 self; + self._tag = detail::AffixFuzzer4Tag::many_required; + new (&self._data.many_required) TypeAlias(std::move(many_required)); + return std::move(self); + } + + static AffixFuzzer4 many_optional( + std::optional> many_optional) { + typedef std::optional> TypeAlias; + AffixFuzzer4 self; + self._tag = detail::AffixFuzzer4Tag::many_optional; + new (&self._data.many_optional) TypeAlias(std::move(many_optional)); + return std::move(self); + } + + void swap(AffixFuzzer4& other) noexcept { + auto tag_temp = this->_tag; + this->_tag = other._tag; + other._tag = tag_temp; + this->_data.swap(other._data); + } + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/affix_fuzzer5.hpp b/rerun_cpp/src/datatypes/affix_fuzzer5.hpp index 952cd1bd3147..894d50aa7ada 100644 --- a/rerun_cpp/src/datatypes/affix_fuzzer5.hpp +++ b/rerun_cpp/src/datatypes/affix_fuzzer5.hpp @@ -3,8 +3,19 @@ #pragma once +#include +#include +#include + +#include "../datatypes/affix_fuzzer4.hpp" + namespace rr { namespace datatypes { - struct AffixFuzzer5 {}; + struct AffixFuzzer5 { + std::optional single_optional_union; + + AffixFuzzer5(std::optional single_optional_union) + : single_optional_union(std::move(single_optional_union)) {} + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/angle.hpp b/rerun_cpp/src/datatypes/angle.hpp index 3b40d1f3ad62..3a48962c5e09 100644 --- a/rerun_cpp/src/datatypes/angle.hpp +++ b/rerun_cpp/src/datatypes/angle.hpp @@ -3,8 +3,80 @@ #pragma once +#include +#include +#include + namespace rr { namespace datatypes { - struct Angle {}; + namespace detail { + enum class AngleTag { + /// Having a special empty state makes it possible to implement move-semantics. We + /// need to be able to leave the object in a state which we can run the destructor + /// on. + NONE = 0, + Radians, + Degrees, + }; + + union AngleData { + float radians; + + float degrees; + + AngleData() {} + + ~AngleData() {} + + void swap(AngleData& other) noexcept { + // This bitwise swap would fail for self-referential types, but we don't have + // any of those. + char temp[sizeof(AngleData)]; + std::memcpy(temp, this, sizeof(AngleData)); + std::memcpy(this, &other, sizeof(AngleData)); + std::memcpy(&other, temp, sizeof(AngleData)); + } + }; + } // namespace detail + + /// Angle in either radians or degrees. + struct Angle { + private: + detail::AngleTag _tag; + detail::AngleData _data; + + Angle() : _tag(detail::AngleTag::NONE) {} + + public: + Angle(Angle&& other) noexcept : _tag(detail::AngleTag::NONE) { + this->swap(other); + } + + Angle& operator=(Angle&& other) noexcept { + this->swap(other); + return *this; + } + + static Angle radians(float radians) { + Angle self; + self._tag = detail::AngleTag::Radians; + self._data.radians = std::move(radians); + return std::move(self); + } + + static Angle degrees(float degrees) { + Angle self; + self._tag = detail::AngleTag::Degrees; + self._data.degrees = std::move(degrees); + return std::move(self); + } + + void swap(Angle& other) noexcept { + auto tag_temp = this->_tag; + this->_tag = other._tag; + other._tag = tag_temp; + this->_data.swap(other._data); + } + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/flattened_scalar.hpp b/rerun_cpp/src/datatypes/flattened_scalar.hpp index 228f6458ccde..dec5ff7d6c85 100644 --- a/rerun_cpp/src/datatypes/flattened_scalar.hpp +++ b/rerun_cpp/src/datatypes/flattened_scalar.hpp @@ -3,8 +3,15 @@ #pragma once +#include +#include + namespace rr { namespace datatypes { - struct FlattenedScalar {}; + struct FlattenedScalar { + float value; + + FlattenedScalar(float value) : value(std::move(value)) {} + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/mat3x3.hpp b/rerun_cpp/src/datatypes/mat3x3.hpp index 51e668b5c0cd..6ba355d6362b 100644 --- a/rerun_cpp/src/datatypes/mat3x3.hpp +++ b/rerun_cpp/src/datatypes/mat3x3.hpp @@ -3,8 +3,13 @@ #pragma once +#include + namespace rr { namespace datatypes { - struct Mat3x3 {}; + /// A 3x3 column-major Matrix. + struct Mat3x3 { + float coeffs[9]; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/mat4x4.hpp b/rerun_cpp/src/datatypes/mat4x4.hpp index a29026bbe04e..d017037a2aa2 100644 --- a/rerun_cpp/src/datatypes/mat4x4.hpp +++ b/rerun_cpp/src/datatypes/mat4x4.hpp @@ -3,8 +3,13 @@ #pragma once +#include + namespace rr { namespace datatypes { - struct Mat4x4 {}; + /// A 4x4 column-major Matrix. + struct Mat4x4 { + float coeffs[16]; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/point2d.hpp b/rerun_cpp/src/datatypes/point2d.hpp index 0c2d86919729..48a4aa7b90bf 100644 --- a/rerun_cpp/src/datatypes/point2d.hpp +++ b/rerun_cpp/src/datatypes/point2d.hpp @@ -3,8 +3,15 @@ #pragma once +#include + namespace rr { namespace datatypes { - struct Point2D {}; + /// A point in 2D space. + struct Point2D { + float x; + + float y; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/quaternion.hpp b/rerun_cpp/src/datatypes/quaternion.hpp index c69fb074e7c4..45397bb64a2e 100644 --- a/rerun_cpp/src/datatypes/quaternion.hpp +++ b/rerun_cpp/src/datatypes/quaternion.hpp @@ -3,8 +3,13 @@ #pragma once +#include + namespace rr { namespace datatypes { - struct Quaternion {}; + /// A Quaternion represented by 4 real numbers. + struct Quaternion { + float xyzw[4]; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/rotation3d.hpp b/rerun_cpp/src/datatypes/rotation3d.hpp index 1df8f7d44194..1425ec15acd1 100644 --- a/rerun_cpp/src/datatypes/rotation3d.hpp +++ b/rerun_cpp/src/datatypes/rotation3d.hpp @@ -3,8 +3,97 @@ #pragma once +#include +#include +#include + +#include "../datatypes/quaternion.hpp" +#include "../datatypes/rotation_axis_angle.hpp" + namespace rr { namespace datatypes { - struct Rotation3D {}; + namespace detail { + enum class Rotation3DTag { + /// Having a special empty state makes it possible to implement move-semantics. We + /// need to be able to leave the object in a state which we can run the destructor + /// on. + NONE = 0, + Quaternion, + AxisAngle, + }; + + union Rotation3DData { + /// Rotation defined by a quaternion. + rr::datatypes::Quaternion quaternion; + + /// Rotation defined with an axis and an angle. + rr::datatypes::RotationAxisAngle axis_angle; + + Rotation3DData() {} + + ~Rotation3DData() {} + + void swap(Rotation3DData& other) noexcept { + // This bitwise swap would fail for self-referential types, but we don't have + // any of those. + char temp[sizeof(Rotation3DData)]; + std::memcpy(temp, this, sizeof(Rotation3DData)); + std::memcpy(this, &other, sizeof(Rotation3DData)); + std::memcpy(&other, temp, sizeof(Rotation3DData)); + } + }; + } // namespace detail + + /// A 3D rotation. + struct Rotation3D { + private: + detail::Rotation3DTag _tag; + detail::Rotation3DData _data; + + Rotation3D() : _tag(detail::Rotation3DTag::NONE) {} + + public: + Rotation3D(Rotation3D&& other) noexcept : _tag(detail::Rotation3DTag::NONE) { + this->swap(other); + } + + Rotation3D& operator=(Rotation3D&& other) noexcept { + this->swap(other); + return *this; + } + + /// Rotation defined by a quaternion. + static Rotation3D quaternion(rr::datatypes::Quaternion quaternion) { + Rotation3D self; + self._tag = detail::Rotation3DTag::Quaternion; + self._data.quaternion = std::move(quaternion); + return std::move(self); + } + + /// Rotation defined with an axis and an angle. + static Rotation3D axis_angle(rr::datatypes::RotationAxisAngle axis_angle) { + Rotation3D self; + self._tag = detail::Rotation3DTag::AxisAngle; + self._data.axis_angle = std::move(axis_angle); + return std::move(self); + } + + /// Rotation defined by a quaternion. + Rotation3D(rr::datatypes::Quaternion quaternion) { + *this = Rotation3D::quaternion(std::move(quaternion)); + } + + /// Rotation defined with an axis and an angle. + Rotation3D(rr::datatypes::RotationAxisAngle axis_angle) { + *this = Rotation3D::axis_angle(std::move(axis_angle)); + } + + void swap(Rotation3D& other) noexcept { + auto tag_temp = this->_tag; + this->_tag = other._tag; + other._tag = tag_temp; + this->_data.swap(other._data); + } + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/rotation_axis_angle.hpp b/rerun_cpp/src/datatypes/rotation_axis_angle.hpp index 35eccd762452..8c3756ac4624 100644 --- a/rerun_cpp/src/datatypes/rotation_axis_angle.hpp +++ b/rerun_cpp/src/datatypes/rotation_axis_angle.hpp @@ -3,8 +3,24 @@ #pragma once +#include + +#include "../datatypes/angle.hpp" +#include "../datatypes/vec3d.hpp" + namespace rr { namespace datatypes { - struct RotationAxisAngle {}; + /// 3D rotation represented by a rotation around a given axis. + struct RotationAxisAngle { + /// Axis to rotate around. + /// + /// This is not required to be normalized. + /// If normalization fails (typically because the vector is length zero), the rotation + /// is silently ignored. + rr::datatypes::Vec3D axis; + + /// How much to rotate around the axis. + rr::datatypes::Angle angle; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/scale3d.hpp b/rerun_cpp/src/datatypes/scale3d.hpp index 2d520a151037..5cdb5e34c4a0 100644 --- a/rerun_cpp/src/datatypes/scale3d.hpp +++ b/rerun_cpp/src/datatypes/scale3d.hpp @@ -3,8 +3,96 @@ #pragma once +#include +#include +#include + +#include "../datatypes/vec3d.hpp" + namespace rr { namespace datatypes { - struct Scale3D {}; + namespace detail { + enum class Scale3DTag { + /// Having a special empty state makes it possible to implement move-semantics. We + /// need to be able to leave the object in a state which we can run the destructor + /// on. + NONE = 0, + ThreeD, + Uniform, + }; + + union Scale3DData { + /// Individual scaling factors for each axis, distorting the original object. + rr::datatypes::Vec3D three_d; + + /// Uniform scaling factor along all axis. + float uniform; + + Scale3DData() {} + + ~Scale3DData() {} + + void swap(Scale3DData& other) noexcept { + // This bitwise swap would fail for self-referential types, but we don't have + // any of those. + char temp[sizeof(Scale3DData)]; + std::memcpy(temp, this, sizeof(Scale3DData)); + std::memcpy(this, &other, sizeof(Scale3DData)); + std::memcpy(&other, temp, sizeof(Scale3DData)); + } + }; + } // namespace detail + + /// 3D scaling factor, part of a transform representation. + struct Scale3D { + private: + detail::Scale3DTag _tag; + detail::Scale3DData _data; + + Scale3D() : _tag(detail::Scale3DTag::NONE) {} + + public: + Scale3D(Scale3D&& other) noexcept : _tag(detail::Scale3DTag::NONE) { + this->swap(other); + } + + Scale3D& operator=(Scale3D&& other) noexcept { + this->swap(other); + return *this; + } + + /// Individual scaling factors for each axis, distorting the original object. + static Scale3D three_d(rr::datatypes::Vec3D three_d) { + Scale3D self; + self._tag = detail::Scale3DTag::ThreeD; + self._data.three_d = std::move(three_d); + return std::move(self); + } + + /// Uniform scaling factor along all axis. + static Scale3D uniform(float uniform) { + Scale3D self; + self._tag = detail::Scale3DTag::Uniform; + self._data.uniform = std::move(uniform); + return std::move(self); + } + + /// Individual scaling factors for each axis, distorting the original object. + Scale3D(rr::datatypes::Vec3D three_d) { + *this = Scale3D::three_d(std::move(three_d)); + } + + /// Uniform scaling factor along all axis. + Scale3D(float uniform) { + *this = Scale3D::uniform(std::move(uniform)); + } + + void swap(Scale3D& other) noexcept { + auto tag_temp = this->_tag; + this->_tag = other._tag; + other._tag = tag_temp; + this->_data.swap(other._data); + } + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/transform3d.hpp b/rerun_cpp/src/datatypes/transform3d.hpp index 53ec0bc4aa41..118bffd9bf70 100644 --- a/rerun_cpp/src/datatypes/transform3d.hpp +++ b/rerun_cpp/src/datatypes/transform3d.hpp @@ -3,8 +3,94 @@ #pragma once +#include +#include +#include + +#include "../datatypes/translation_and_mat3x3.hpp" +#include "../datatypes/translation_rotation_scale3d.hpp" + namespace rr { namespace datatypes { - struct Transform3D {}; + namespace detail { + enum class Transform3DTag { + /// Having a special empty state makes it possible to implement move-semantics. We + /// need to be able to leave the object in a state which we can run the destructor + /// on. + NONE = 0, + TranslationAndMat3x3, + TranslationRotationScale, + }; + + union Transform3DData { + rr::datatypes::TranslationAndMat3x3 translation_and_mat3x3; + + rr::datatypes::TranslationRotationScale3D translation_rotation_scale; + + Transform3DData() {} + + ~Transform3DData() {} + + void swap(Transform3DData& other) noexcept { + // This bitwise swap would fail for self-referential types, but we don't have + // any of those. + char temp[sizeof(Transform3DData)]; + std::memcpy(temp, this, sizeof(Transform3DData)); + std::memcpy(this, &other, sizeof(Transform3DData)); + std::memcpy(&other, temp, sizeof(Transform3DData)); + } + }; + } // namespace detail + + /// Representation of a 3D affine transform. + struct Transform3D { + private: + detail::Transform3DTag _tag; + detail::Transform3DData _data; + + Transform3D() : _tag(detail::Transform3DTag::NONE) {} + + public: + Transform3D(Transform3D&& other) noexcept : _tag(detail::Transform3DTag::NONE) { + this->swap(other); + } + + Transform3D& operator=(Transform3D&& other) noexcept { + this->swap(other); + return *this; + } + + static Transform3D translation_and_mat3x3( + rr::datatypes::TranslationAndMat3x3 translation_and_mat3x3) { + Transform3D self; + self._tag = detail::Transform3DTag::TranslationAndMat3x3; + self._data.translation_and_mat3x3 = std::move(translation_and_mat3x3); + return std::move(self); + } + + static Transform3D translation_rotation_scale( + rr::datatypes::TranslationRotationScale3D translation_rotation_scale) { + Transform3D self; + self._tag = detail::Transform3DTag::TranslationRotationScale; + self._data.translation_rotation_scale = std::move(translation_rotation_scale); + return std::move(self); + } + + Transform3D(rr::datatypes::TranslationAndMat3x3 translation_and_mat3x3) { + *this = Transform3D::translation_and_mat3x3(std::move(translation_and_mat3x3)); + } + + Transform3D(rr::datatypes::TranslationRotationScale3D translation_rotation_scale) { + *this = + Transform3D::translation_rotation_scale(std::move(translation_rotation_scale)); + } + + void swap(Transform3D& other) noexcept { + auto tag_temp = this->_tag; + this->_tag = other._tag; + other._tag = tag_temp; + this->_data.swap(other._data); + } + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/translation_and_mat3x3.hpp b/rerun_cpp/src/datatypes/translation_and_mat3x3.hpp index a4e568754377..2395bf4aea83 100644 --- a/rerun_cpp/src/datatypes/translation_and_mat3x3.hpp +++ b/rerun_cpp/src/datatypes/translation_and_mat3x3.hpp @@ -3,8 +3,27 @@ #pragma once +#include +#include + +#include "../datatypes/mat3x3.hpp" +#include "../datatypes/vec3d.hpp" + namespace rr { namespace datatypes { - struct TranslationAndMat3x3 {}; + /// Representation of an affine transform via a 3x3 affine matrix paired with a translation. + /// + /// First applies the matrix, then the translation. + struct TranslationAndMat3x3 { + /// 3D translation, applied after the matrix. + std::optional translation; + + /// 3x3 matrix for scale, rotation & shear. + std::optional matrix; + + /// If true, the transform maps from the parent space to the space where the transform + /// was logged. Otherwise, the transform maps from the space to its parent. + bool from_parent; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/translation_rotation_scale3d.hpp b/rerun_cpp/src/datatypes/translation_rotation_scale3d.hpp index 6803bf2fc83b..c5c8947525c3 100644 --- a/rerun_cpp/src/datatypes/translation_rotation_scale3d.hpp +++ b/rerun_cpp/src/datatypes/translation_rotation_scale3d.hpp @@ -3,8 +3,29 @@ #pragma once +#include +#include + +#include "../datatypes/rotation3d.hpp" +#include "../datatypes/scale3d.hpp" +#include "../datatypes/vec3d.hpp" + namespace rr { namespace datatypes { - struct TranslationRotationScale3D {}; + /// Representation of an affine transform via separate translation, rotation & scale. + struct TranslationRotationScale3D { + /// 3D translation vector, applied last. + std::optional translation; + + /// 3D rotation, applied second. + std::optional rotation; + + /// 3D scale, applied first. + std::optional scale; + + /// If true, the transform maps from the parent space to the space where the transform + /// was logged. Otherwise, the transform maps from the space to its parent. + bool from_parent; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/vec2d.hpp b/rerun_cpp/src/datatypes/vec2d.hpp index 3f20c52cef0c..91bac0c252c5 100644 --- a/rerun_cpp/src/datatypes/vec2d.hpp +++ b/rerun_cpp/src/datatypes/vec2d.hpp @@ -3,8 +3,13 @@ #pragma once +#include + namespace rr { namespace datatypes { - struct Vec2D {}; + /// A vector in 2D space. + struct Vec2D { + float xy[2]; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/vec3d.hpp b/rerun_cpp/src/datatypes/vec3d.hpp index 3ef5ecea8850..c65bd08eea12 100644 --- a/rerun_cpp/src/datatypes/vec3d.hpp +++ b/rerun_cpp/src/datatypes/vec3d.hpp @@ -3,8 +3,13 @@ #pragma once +#include + namespace rr { namespace datatypes { - struct Vec3D {}; + /// A vector in 3D space. + struct Vec3D { + float xyz[3]; + }; } // namespace datatypes } // namespace rr diff --git a/rerun_cpp/src/datatypes/vec4d.hpp b/rerun_cpp/src/datatypes/vec4d.hpp index 435d65504f9c..cebf2e4da04e 100644 --- a/rerun_cpp/src/datatypes/vec4d.hpp +++ b/rerun_cpp/src/datatypes/vec4d.hpp @@ -3,8 +3,13 @@ #pragma once +#include + namespace rr { namespace datatypes { - struct Vec4D {}; + /// A vector in 4D space. + struct Vec4D { + float xyzw[4]; + }; } // namespace datatypes } // namespace rr diff --git a/scripts/lint.py b/scripts/lint.py index 85760b28b3e8..bd00f6f97b41 100755 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -17,7 +17,7 @@ # ----------------------------------------------------------------------------- -todo_pattern = re.compile(r"TODO([^(]|$)") +todo_pattern = re.compile(r'TODO([^_"(]|$)') debug_format_of_err = re.compile(r"\{\:#?\?\}.*, err") error_match_name = re.compile(r"Err\((\w+)\)") error_map_err_name = re.compile(r"map_err\(\|(\w+)\|") @@ -56,9 +56,6 @@ def lint_line(line: str, file_extension: str = "rs") -> str | None: if "todo!()" in line: return 'todo!() should be written as todo!("$details")' - if "unimplemented!" in line: - return "unimplemented!(): either implement this, or rewrite it as a todo!()" - if todo_pattern.search(line): return "TODO:s should be written as `TODO(yourname): what to do`" @@ -102,6 +99,7 @@ def test_lint_line() -> None: "todo lowercase is fine", 'todo!("macro is ok with text")', "TODO(emilk):", + "TODO_TOKEN", 'eprintln!("{:?}, {err}", foo)', 'eprintln!("{:#?}, {err}", foo)', 'eprintln!("{err}")', @@ -125,8 +123,7 @@ def test_lint_line() -> None: "HACK", "TODO", "TODO:", - "todo!()" "unimplemented!()", - 'unimplemented!("even with text!")', + "todo!()", 'eprintln!("{err:?}")', 'eprintln!("{err:#?}")', 'eprintln!("{:?}", err)',