From 5580b5112f3f70834615c8ce870129a448142c29 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 27 May 2020 16:17:12 +0200 Subject: [PATCH] Fix number to string conversion in C++ --- api/sixtyfps-cpp/include/sixtyfps_string.h | 4 + examples/cpptest/hello.60 | 2 +- examples/cpptest/main.cpp | 6 -- sixtyfps_compiler/generator/cpp.rs | 39 +++++---- sixtyfps_runtime/corelib/abi/string.rs | 98 ++++++++++++++-------- 5 files changed, 90 insertions(+), 59 deletions(-) diff --git a/api/sixtyfps-cpp/include/sixtyfps_string.h b/api/sixtyfps-cpp/include/sixtyfps_string.h index 065b20a6d..f2d52bdda 100644 --- a/api/sixtyfps-cpp/include/sixtyfps_string.h +++ b/api/sixtyfps-cpp/include/sixtyfps_string.h @@ -31,7 +31,11 @@ struct SharedString operator std::string_view() const { return internal::sixtyfps_shared_string_bytes(this); } auto data() const -> const char * { return internal::sixtyfps_shared_string_bytes(this); } + static SharedString from_number(double n) { return SharedString(n); } + private: + /// Use SharedString::from_number + explicit SharedString(double n) { internal::sixtyfps_shared_string_from_number(this, n); } void *inner; // opaque }; } diff --git a/examples/cpptest/hello.60 b/examples/cpptest/hello.60 index 457078e2d..c9456e121 100644 --- a/examples/cpptest/hello.60 +++ b/examples/cpptest/hello.60 @@ -79,7 +79,7 @@ Hello := Rectangle { button_text: "+"; } property counter; - counter_label := Text { x: 100; y: 300; text: "0"; color: black; } + counter_label := Text { x: 100; y: 300; text: counter; color: black; } ButtonRectangle { color: 4289374890; x: 50; diff --git a/examples/cpptest/main.cpp b/examples/cpptest/main.cpp index 3ba1bdd6b..773b77145 100644 --- a/examples/cpptest/main.cpp +++ b/examples/cpptest/main.cpp @@ -11,17 +11,11 @@ int main() { component.plus_clicked.set_handler([](auto...){ auto &counter = component.counter; counter.set(counter.get() + 1); - // FIXME: this _13 is an internal detail and should be private anyway. We muse use some - // alias or way to expose the property (same for the _ before signals) - component.counter_label_11.text.set(std::string_view(std::to_string(counter.get()))); - std::cout << "PLUS: " << std::string_view(component.counter_label_11.text.get()) << std::endl; }); component.minus_clicked.set_handler([](auto...){ auto &counter = component.counter; counter.set(counter.get() - 1); - component.counter_label_11.text.set(std::string_view(std::to_string(counter.get()))); - std::cout << "MINUS: " << std::string_view(component.counter_label_11.text.get()) << std::endl; }); sixtyfps::run(&component); diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index c5868a2a3..49ed19f4a 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -162,7 +162,7 @@ fn handle_item( let id = &item.id; init.extend(item.bindings.iter().map(|(s, i)| { - use crate::expression_tree::{Expression, Expression::*}; + use crate::expression_tree::Expression; match i { Expression::SignalReference { component:_, element:_, name } => { let signal_accessor_prefix = if item.signals_declaration.contains(s) { @@ -183,14 +183,7 @@ fn handle_item( format!("{id}.", id = id.clone()) }; - let init = match &i { - StringLiteral(s) => { - format!(r#"sixtyfps::SharedString("{}")"#, s.escape_default()) - } - NumberLiteral(n) => n.to_string(), - PropertyReference { name, .. } => format!(r#"{}.get()"#, name), - _ => format!("\n#error: unsupported expression {:?}\n", i), - }; + let init = compile_expression(i); format!( "{accessor_prefix}{cpp_prop}.set({init});", accessor_prefix = accessor_prefix, @@ -298,18 +291,28 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option int".to_owned(), - statements: Some(vec![ - format!("static {} component;", component.id), - format!("sixtyfps::run(&component);"), - ]), - ..Default::default() - }));*/ if diag.has_error() { None } else { Some(x) } } + +fn compile_expression(e: &crate::expression_tree::Expression) -> String { + use crate::expression_tree::Expression::*; + match e { + StringLiteral(s) => format!(r#"sixtyfps::SharedString("{}")"#, s.escape_default()), + NumberLiteral(n) => n.to_string(), + PropertyReference { name, .. } => format!(r#"{}.get()"#, name), + Cast { from, to } => { + let f = compile_expression(&*from); + match (from.ty(), to) { + (Type::Float32, Type::String) | (Type::Int32, Type::String) => { + format!("sixtyfps::SharedString::from_number({})", f) + } + _ => f, + } + } + _ => format!("\n#error: unsupported expression {:?}\n", e), + } +} diff --git a/sixtyfps_runtime/corelib/abi/string.rs b/sixtyfps_runtime/corelib/abi/string.rs index 503df425b..917e6c745 100644 --- a/sixtyfps_runtime/corelib/abi/string.rs +++ b/sixtyfps_runtime/corelib/abi/string.rs @@ -228,40 +228,6 @@ where impl Eq for SharedString {} -/// for cbingen. -#[allow(non_camel_case_types)] -type c_char = u8; - -#[no_mangle] -pub extern "C" fn sixtyfps_shared_string_bytes(ss: &SharedString) -> *const c_char { - ss.as_ptr() -} - -#[no_mangle] -/// Destroy the shared string -pub unsafe extern "C" fn sixtyfps_shared_string_drop(ss: *const SharedString) { - core::ptr::read(ss); -} - -#[no_mangle] -/// Increment the reference count of the string. -/// the resulting structure must be passed to sixtyfps_shared_string_drop -pub unsafe extern "C" fn sixtyfps_shared_string_clone(out: *mut SharedString, ss: &SharedString) { - core::ptr::write(out, ss.clone()) -} - -#[no_mangle] -/// Safety: bytes must be a valid utf-8 string of size len wihout null inside. -/// the resulting structure must be passed to sixtyfps_shared_string_drop -pub unsafe extern "C" fn sixtyfps_shared_string_from_bytes( - out: *mut SharedString, - bytes: *const c_char, - len: usize, -) { - let str = core::str::from_utf8_unchecked(core::slice::from_raw_parts(bytes, len)); - core::ptr::write(out, SharedString::from(str)); -} - #[test] fn simple_test() { let x = SharedString::from("hello world!"); @@ -279,3 +245,67 @@ fn simple_test() { &*std::ffi::CString::new("hello world!").unwrap() ); } + +/// for cbingen. +#[allow(non_camel_case_types)] +type c_char = u8; + +#[no_mangle] +pub extern "C" fn sixtyfps_shared_string_bytes(ss: &SharedString) -> *const c_char { + ss.as_ptr() +} + +#[no_mangle] +/// Destroy the shared string +pub unsafe extern "C" fn sixtyfps_shared_string_drop(ss: *const SharedString) { + core::ptr::read(ss); +} + +#[no_mangle] +/// Increment the reference count of the string. +/// The resulting structure must be passed to sixtyfps_shared_string_drop +pub unsafe extern "C" fn sixtyfps_shared_string_clone(out: *mut SharedString, ss: &SharedString) { + core::ptr::write(out, ss.clone()) +} + +#[no_mangle] +/// Safety: bytes must be a valid utf-8 string of size len wihout null inside. +/// The resulting structure must be passed to sixtyfps_shared_string_drop +pub unsafe extern "C" fn sixtyfps_shared_string_from_bytes( + out: *mut SharedString, + bytes: *const c_char, + len: usize, +) { + let str = core::str::from_utf8_unchecked(core::slice::from_raw_parts(bytes, len)); + core::ptr::write(out, SharedString::from(str)); +} + +/// Create a string from a number. +/// The resulting structure must be passed to sixtyfps_shared_string_drop +#[no_mangle] +pub unsafe extern "C" fn sixtyfps_shared_string_from_number(out: *mut SharedString, n: f64) { + // TODO: implement Write for SharedString so this can be done without alocation + let str = format!("{}", n); + core::ptr::write(out, SharedString::from(str.as_str())); +} + +#[test] +fn test_sixtyfps_shared_string_from_number() { + unsafe { + let mut s = core::mem::MaybeUninit::uninit(); + sixtyfps_shared_string_from_number(s.as_mut_ptr(), 45.); + assert_eq!(s.assume_init(), "45"); + + let mut s = core::mem::MaybeUninit::uninit(); + sixtyfps_shared_string_from_number(s.as_mut_ptr(), 45.12); + assert_eq!(s.assume_init(), "45.12"); + + let mut s = core::mem::MaybeUninit::uninit(); + sixtyfps_shared_string_from_number(s.as_mut_ptr(), -1325466.); + assert_eq!(s.assume_init(), "-1325466"); + + let mut s = core::mem::MaybeUninit::uninit(); + sixtyfps_shared_string_from_number(s.as_mut_ptr(), -0.); + assert_eq!(s.assume_init(), "0"); + } +}