diff --git a/cpp-overload-test/src/lib.rs b/cpp-overload-test/src/lib.rs index b72552c..dc280df 100644 --- a/cpp-overload-test/src/lib.rs +++ b/cpp-overload-test/src/lib.rs @@ -8,4 +8,5 @@ pub mod stdcpp { // Add new modules for each type here, in alphabetical order. pub mod forward_list; + pub mod time_point; } diff --git a/cpp-overload-test/src/stdcpp/time_point.rs b/cpp-overload-test/src/stdcpp/time_point.rs new file mode 100644 index 0000000..b3642bc --- /dev/null +++ b/cpp-overload-test/src/stdcpp/time_point.rs @@ -0,0 +1,149 @@ +//! C++ overloads tests for [std::chrono::time_point][time_point] +//! +//! This file tests three constructors that can be represented in Rust. +//! +//! ### Limitations +//! +//! C++ time points use clock and duration types. +//! This test uses fixed types instead of supporting all possible types. +//! +//! [time_point]: https://en.cppreference.com/cpp/chrono/time_point + +#![allow(unused_braces, reason = "Generated by overload! macro")] + +use cpp::cpp; +use splat_overload::overload; +use std::ffi::{c_longlong, c_void}; +use std::marker::PhantomData; + +cpp! {{ + #include + + using TestClock = std::chrono::system_clock; + using TestDuration = std::chrono::milliseconds; + using TestTimePoint = + std::chrono::time_point; + + using TestSecondsTimePoint = + std::chrono::time_point; +}} + +/// A wrapper around a C++ time point with millisecond precision. +#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] +pub struct StdTimePoint(*mut c_void, PhantomData<()>); + +/// A Wrapper around a C++ time point with second precision. +#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] +pub struct StdSecondsTimePoint(*mut c_void, PhantomData<()>); + +#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] +impl StdSecondsTimePoint { + /// Construct a time point from seconds since the epoch. + fn from_seconds(seconds: i64) -> Self { + let point = unsafe { + cpp!([seconds as "long long"] -> *mut c_void as + "TestSecondsTimePoint*" { + return new TestSecondsTimePoint( + std::chrono::seconds(seconds) + ); + }) + }; + + Self(point, PhantomData) + } +} + +#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] +impl StdTimePoint { + /// Construct a time point at the clock's epoch. + fn default() -> Self { + let point = unsafe { + cpp!([] -> *mut c_void as "TestTimePoint*" { + return new TestTimePoint(); + }) + }; + + Self(point, PhantomData) + } + + /// Construct a time point from milliseconds since the epoch. + fn from_duration(milliseconds: i64) -> Self { + let point = unsafe { + cpp!([milliseconds as "long long"] -> *mut c_void as + "TestTimePoint*" { + return new TestTimePoint( + TestDuration(milliseconds) + ); + }) + }; + + Self(point, PhantomData) + } + + /// Construct a millisecond time point from second time point. + fn from_other(other: &StdSecondsTimePoint) -> Self { + let other = other.0 as *const c_void; + + let point = unsafe { + cpp!([other as "const TestSecondsTimePoint*"] -> *mut + c_void as "TestTimePoint*" { + return new TestTimePoint(*other); + + }) + }; + + Self(point, PhantomData) + } + + /// Return the number of milliseconds since epoch. + fn milliseconds_since_epoch(&self) -> i64 { + let point = self.0 as *const c_void; + + unsafe { + cpp!([point as "const TestTimePoint*"] -> c_longlong as + "long long" { + return point->time_since_epoch().count(); + }) + } + } +} + +// ### Ergonomics +// C++ allows constructors with the same name but different arguments. +// +// Rust does not normallly support constructor overloading. +// The overload! macro allows these constructors to share the name new. +overload! { + impl StdTimePoint { + /// Construct time point at clock's epoch. + pub fn new() -> StdTimePoint { + StdTimePoint::default() + } + + /// Construct time point from milliseconds since the epoch. + pub fn new(milliseconds: i64) -> StdTimePoint { + StdTimePoint::from_duration(milliseconds) + } + + /// Construct time point from seconds-resolution time point. + pub fn new(other: &StdSecondsTimePoint) -> StdTimePoint { + StdTimePoint::from_other(other) + } + } +} + +#[test] +fn test_time_point_constructors() { + // Test the default constructor. + let epoch = StdTimePoint::new(); + assert_eq!(epoch.milliseconds_since_epoch(), 0); + + // Test the duration constructor. + let milliseconds = StdTimePoint::new(1500_i64); + assert_eq!(milliseconds.milliseconds_since_epoch(), 1500); + + // Test the converting constructor. + let seconds = StdSecondsTimePoint::from_seconds(2); + let converted = StdTimePoint::new(&seconds); + assert_eq!(converted.milliseconds_since_epoch(), 2000); +}