我需要将C++std::chrono::steady_clock::time_point
转换为C++/CLISystem::DateTime
。
背景:
我正在用C++/CLI接口包装一个C++库,供.NET应用程序使用
其中一个C++方法返回一个std::chrono::steady_clock::time_point
。我认为从C++/CLI包装器方法返回一个System::DateTime
是合适的。因此需要转换。
我知道,如果我有一个system_clock::time_point
,
我可以将其转换为time_t
,如下所述:如何将std::chrono::time_point转换为带有小数秒的日历日期时间字符串
然后我可以使用DateTimeOffset.FromUnixTimeMilliseconds,并从中获得System::DateTime
另一种方法可能是使用time_since_epoch
但to_time_t
和time_since_epoch
都不适用于std::chrono::steady_clock
(关于time_since_epoch
,请参阅:chrono steady_clock未给出正确结果?)。
但是,我无法更改C++接口
也未能将steady_clock::time_point
正确转换为例如system_clock::time_point
。
我提出的解决方案:
我从CCD_ 15和CCD_,则从CCD_ 17计算偏移,并最终以与DateTime时间相反的方式应用该偏移。我以毫秒为单位计算偏移量,由于我感兴趣的精度是秒,所以它工作得很好
此方法显示在下面的代码中。
但感觉有点尴尬。它对所要求的精度也很敏感。
我的问题:你能提出一个更好的转换方法吗?
using namespace System;
#include <chrono>
System::DateTime SteadyClockTimePointToDateTime(std::chrono::steady_clock::time_point const & tCPP)
{
auto nowCPP = std::chrono::steady_clock::now();
auto nowCLI = System::DateTime::Now;
long long milliSecsSinceT = std::chrono::duration_cast<std::chrono::milliseconds>(nowCPP - tCPP).count();
System::DateTime tCLI = nowCLI - System::TimeSpan::FromMilliseconds(static_cast<double>(milliSecsSinceT));
return tCLI;
}
int main(array<System::String ^> ^args)
{
System::Console::WriteLine("System::DateTime::Now (for debug): " + System::DateTime::Now.ToString()); // print reference time for debug
auto tCPP = std::chrono::steady_clock::now(); // got the actual value from a C++ lib.
System::Threading::Thread::Sleep(100); // pass some time to simulate stuff that was executed since the time_point was received.
System::DateTime tCLI = SteadyClockTimePointToDateTime(tCPP);
System::Console::WriteLine("System::DateTime (converted): " + tCLI.ToString()); // should show a time very close to System::DateTime::Now above
return 0;
}
输出示例:
System::DateTime::Now (for debug): 23-May-22 16:41:04
System::DateTime (converted): 23-May-22 16:41:04
注意:我添加了C++标记,因为这个问题不是纯粹的C++/CLI问题。例如,可能有一种涉及std::chrono时钟之间转换的解决方案,可以轻松地进一步转换为System::DateTime(如上文关于DateTimeOffset.FromUnixTimeMilliseconds
所述)。
这种方法很好,但只需进行一些小的更改,代码就可以变得更短、更容易阅读:
template<typename Rep, typename Period>
System::TimeSpan DurationToTimeSpan(std::chrono::duration<Rep, Period> const& input)
{
auto milliSecs =
std::chrono::duration_cast<std::chrono::milliseconds>(input).count();
return System::TimeSpan::FromMilliseconds(milliSecs);
}
System::DateTime SteadyClockTimePointToDateTime(
std::chrono::steady_clock::time_point const & tCPP)
{
auto const nowCPP = std::chrono::steady_clock::now();
auto nowCLI = System::DateTime::Now;
auto tCLI = nowCLI + DurationToTimeSpan(tCPP - nowCPP);
}
所做的具体更改包括:
- 隐藏在另一个辅助函数中使用持续时间强制转换和
TimeSpan
工厂函数调用1 - 反转
nowCPP
和tCPP
的相减顺序,以避免以后必须反转符号 - 将
const
添加到具有本机类型且不会更改的局部变量中。遗憾的是,NET类型不是常量正确的,因为const
-ness只受C++/CLI编译器的尊重,而不受编写.NET库的语言的尊重 - 避免在
count()
的返回值和AddMilliseconds
的参数之间进行显式类型转换。如果对于某些平台,存在不同的持续时间表示,并且隐式转换不起作用,那么最好让编译器告诉维护程序员
注意,该函数的结果不提供";稳定时钟";保证为了做到这一点,应该生成一个单独的time_point
/DateTime
对,并将其保存以供以后重用。
1另一种选择是使用AddMilliseconds
成员函数,它取代了对TimeSpan
工厂函数和过载operator-
:的调用
System::DateTime SteadyClockTimePointToDateTime(
std::chrono::steady_clock::time_point const & tCPP)
{
auto const nowCPP = std::chrono::steady_clock::now();
auto nowCLI = System::DateTime::Now;
auto const milliSecsUntilT =
std::chrono::duration_cast<std::chrono::milliseconds>(tCPP - nowCPP).count();
auto tCLI = nowCLI.AddMilliseconds(milliSecsUntilT);
return tCLI;
}