Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cpp-overload-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
149 changes: 149 additions & 0 deletions cpp-overload-test/src/stdcpp/time_point.rs
Original file line number Diff line number Diff line change
@@ -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 <chrono>

using TestClock = std::chrono::system_clock;
using TestDuration = std::chrono::milliseconds;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This specific type alias makes the code harder to read, because it hides that the type is milliseconds.
Instead, delete this alias, and use the milliseconds type directly.

Please also rename TestTimePoint so it is clear it is in milliseconds.

using TestTimePoint =
std::chrono::time_point<TestClock, TestDuration>;

using TestSecondsTimePoint =
std::chrono::time_point<TestClock, std::chrono::seconds>;
}}

/// 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<()>);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, hiding the units makes the type error-prone to use.

Suggested change
pub struct StdTimePoint(*mut c_void, PhantomData<()>);
pub struct StdMillisecondsTimePoint(*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 {
Comment thread
teor2345 marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The i64 to long long type conversion may fail on some platforms. Instead, use the Rust type that is guaranteed to be a long long on all platforms:
https://doc.rust-lang.org/std/ffi/type.c_longlong.html

Please make this change throughout this file.

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);
}
Loading