Use .NET API from D language via C ++ / CLI (old calendar, Japanese calendar processing)
Introduction
I thought about what to do if I want to use .NET API
from ** D language **.
I hope it will be helpful for you.
Thing you want to do
In Previous article, I implemented a calendar display program in ** D language **.
I would like to continue to implement the lunar calendar.
The .NET API
includes the JapaneseCalendar class and the JapaneseLunisolarCalendar Classes are provided, and you can use them to implement lunar and Japanese calendar calculations from scratch. It seems that you do not have to.
How to implement .NET API call from D language
As far as I can tell, I found the easy way to go via C ++ / CLI wrapping
.
This time we will use this method.
- For reference, the information link I searched for to write the article is posted at the end.
It is a method to create a DLL withC #
and call it from ** D language **.
Preparation 1 (Implementation in C #)
Before implementing it in ** D language **, I implemented the process to get the lunar calendar and Japanese calendar in C #
.
If there is no parameter at the time of execution, the lunar calendar or Japanese calendar of today’s date is displayed, and if the date parameter is specified, the lunar calendar or Japanese calendar of that date is displayed.
JCalendar.cs
using System;
using System.Globalization;
public class JCalendar {
static string[] sEra = //Era
{ "", "Meiji", "Taisho", "Showa", "Heisei", "Reiwa" };
static string[] sRokuyo = //Rokuyo
{ "Daan", "Red mouth", "First win", "Tomobiki", "Predecessor", "Buddha extinction" };
static string[] sKanshi = //Heavenly Stems
{ "", "Instep", "B", "Hinoe", "Ding", "戊", "Self", "Gung", "Spicy", "壬", "癸" };
static string[] sChishi = //Zodiac
{ "", "Child", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Not yet", "Monkey", "Rooster", "Dog", "亥" };
public static void Main(string[] args)
{
DateTime newDate;
if ( args.Length >= 3 ){
newDate = new DateTime(
int.Parse(args[0]), int.Parse(args[1]), int.Parse(args[2]));
} else {
newDate = DateTime.Now;
}
Console.WriteLine("Year: {0:d}", newDate);
if ( newDate >= new DateTime(1868, 9, 8) ){
printJapaneseCalendar(newDate);
}
if ( newDate >= new DateTime(1960, 1, 28)
&& newDate <= new DateTime(2050, 1, 22) ){
printJapaneseLunisolarCalendar(newDate);
}
}
static void printJapaneseCalendar(DateTime newDate)
{
JapaneseCalendar jc = new JapaneseCalendar();
int era = jc.GetEra(newDate);
int year = jc.GetYear(newDate);
int month = jc.GetMonth(newDate);
int day = jc.GetDayOfMonth(newDate);
DateTime jDate = new DateTime(year, month, day);
Console.WriteLine("Japanese Calendar: {0} {1:d}", sEra[era], jDate);
}
static void printJapaneseLunisolarCalendar(DateTime newDate)
{
JapaneseLunisolarCalendar jlc = new JapaneseLunisolarCalendar();
int era = jlc.GetEra(newDate);
int year = jlc.GetYear(newDate);
int month = jlc.GetMonth(newDate);
int day = jlc.GetDayOfMonth(newDate);
//Get leap month
string sLeap = "";
if ( year > 0 ){
int leapMonth = jlc.GetLeapMonth(year, era);
if ( month == leapMonth ){
sLeap = "(Leap month)";
}
//Correct the month when including leap months
if ( (leapMonth > 0) && (month >= leapMonth) ){
month = month - 1; //Lunar month correction
}
}
//Zodiac (Heavenly Stems, Zodiac)
int sy = jlc.GetSexagenaryYear(newDate);
int tk = jlc.GetCelestialStem(sy);
int ts = jlc.GetTerrestrialBranch(sy);
//Rokuyo(Daan, Akaguchi, First victory, Tomobiki, First defeat, Buddha extinction)
// (Month+Day) %6 remainder
int rokuyo = (month + day) % 6;
Console.WriteLine("Lunar calendar: {0} {1:d4}/{2:d2}/{3:d2} {4}",
sEra[era], year, month, day, sLeap);
Console.WriteLine("Zodiac: {0}{1}", sKanshi[tk], sChishi[ts]);
Console.WriteLine("Rokuyo: {0}", sRokuyo[rokuyo]);
}
}
C # compiler (csc.exe)
is installed as standard in Window10
.
Which version is installed in which folder depends on your environment.
Here is an example of compiling and executing in my environment.
In the lunar calendar, there is a leap month
. Use GetLeapMonth
to calculate the leap month
for the year and correct the month.
In the execution result, the month is corrected and displayed correctly. * Verify with Legendary calendar
The zodiac signs (heavenly stems, zodiac signs) have acquired the zodiac signs of the year. It seems that the zodiac also exists on the moon and the day.
Execution result
d:\Dev>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe JCalendar.cs
Microsoft (R) Visual C# Compiler version 4.8.3752.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.
This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240
d:\Dev>JCalendar
Year: 2020/09/26
Japanese Calendar:Reiwa 0002/09/26
Lunar calendar:Reiwa 0002/08/10
Zodiac:Yang Metal Rat
Rokuyo:Daan
d:\Dev>JCalendar 2020 5 23
Year: 2020/05/23
Japanese Calendar:Reiwa 0002/05/23
Lunar calendar:Reiwa 0002/04/01 (leap month)
Zodiac:Yang Metal Rat
Rokuyo:Buddha extinction
Preparation 2 (Implementation in C ++ / CLI)
Next, I implemented the process to get the lunar calendar and Japanese calendar with C ++ / CLI
.
The information about C ++ / CLI
in Japanese is not so much, so I hope it will be helpful.
This is an image of porting the source code of C #
from Preparation 1.
If there is no parameter at the time of execution, the lunar calendar or Japanese calendar of today’s date is displayed, and if the date parameter is specified, the lunar calendar or Japanese calendar of that date is displayed.
Unique to C ++ / CLI
is the operator (^)
.
The handle declarator (^ is “hat”) changes the type specifier to mean that the declared object is automatically deleted when the system determines that the object is inaccessible. I will.
Handle to Object Operator (^) (C ++ / CLI and C ++ / CX)
Also, gcnew
is used for class generation of .NET API
.
Managed (reference or value) memory is allocated by gcnew and freed by garbage collection.
ref new, gcnew (C ++ / CLI and C ++ / CX)
JCal.cpp
using namespace System;
using namespace System::Globalization;
#include <string>
String^ getEraStr(int era)
{ //Era
array<String^>^ sEra = gcnew array<String^>
{ "", "Meiji", "Taisho", "Showa", "Heisei", "Reiwa" };
return ( sEra[era] );
}
String^ getRokuyoStr(int rokuyo)
{ //Rokuyo
array<String^>^ sRokuyo =
{ "Daan", "Red mouth", "First win", "Tomobiki", "Predecessor", "Buddha extinction" };
return ( sRokuyo[rokuyo] );
}
String^ getKanshiStr(int kanshi)
{ //Heavenly Stems
array<String^>^ sKanshi =
{ "", "Instep", "B", "Hinoe", "Ding", "戊", "Self", "Gung", "Spicy", "壬", "癸" };
return ( sKanshi[kanshi] );
}
String^ getChishiStr(int chishi)
{ //Zodiac
array<String^>^sChishi =
{ "", "Child", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Not yet", "Monkey", "Rooster", "Dog", "亥" };
return ( sChishi[chishi] );
}
void printJapaneseCalendar(DateTime newDate)
{
JapaneseCalendar^ jc = gcnew JapaneseCalendar();
int era = jc->GetEra(newDate);
int year = jc->GetYear(newDate);
int month = jc->GetMonth(newDate);
int day = jc->GetDayOfMonth(newDate);
DateTime jDate = DateTime::DateTime(year, month, day);
Console::WriteLine("Japanese Calendar: {0} {1:d}", getEraStr(era), jDate);
}
void printJapaneseLunisolarCalendar(DateTime newDate)
{
JapaneseLunisolarCalendar^ jlc = gcnew JapaneseLunisolarCalendar();
int era = jlc->GetEra(newDate);
int year = jlc->GetYear(newDate);
int month = jlc->GetMonth(newDate);
int day = jlc->GetDayOfMonth(newDate);
//Get leap month
String^ sLeap = "";
if ( year > 0 ){
int leapMonth = jlc->GetLeapMonth(year, era);
if ( month == leapMonth ){
sLeap = "(Leap month)";
}
//Correct the month when including leap months
if ( (leapMonth > 0) && (month >= leapMonth) ){
month = month - 1; //Lunar month correction
}
}
//Zodiac (Heavenly Stems, Zodiac)
int sy = jlc->GetSexagenaryYear(newDate);
int tk = jlc->GetCelestialStem(sy);
int ts = jlc->GetTerrestrialBranch(sy);
//Rokuyo(Daan, Akaguchi, First victory, Tomobiki, First defeat, Buddha extinction)
// (Month+Day) %6 remainder
int rokuyo = (month + day) % 6;
Console::WriteLine("Lunar calendar: {0} {1:d4}/{2:d2}/{3:d2} {4}",
getEraStr(era), year, month, day, sLeap);
Console::WriteLine("Zodiac: {0}{1}", getKanshiStr(tk), getChishiStr(ts));
Console::WriteLine("Rokuyo: {0}", getRokuyoStr(rokuyo));
}
int main(int argc, char* argv[])
{
DateTime newDate = DateTime::Now;
if ( argc > 3 ){
newDate = DateTime::DateTime(
atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
}
Console::WriteLine("Year: {0:d}", newDate);
if ( newDate >= DateTime::DateTime(1868, 9, 8) ){
printJapaneseCalendar(newDate);
}
if ( newDate >= DateTime::DateTime(1960, 1, 28)
&& newDate <= DateTime::DateTime(2050, 1, 22) ){
printJapaneseLunisolarCalendar(newDate);
}
}
I have Visual C ++ Build Tools 2019 (https://visualstudio.microsoft.com/en/downloads/) installed in my environment.
Launch the x64 Native Tools Command Prompt for VS2019 (https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019).
Compile with the compile option / clr
to use the Common Language Runtime (CLR) (https://docs.microsoft.com/en-us/dotnet/standard/clr) feature.
According to the lunar calendar, Reiwa seems to start on March 27th. * Verify with Legendary calendar
Execution result
d:\Dev>cl /clr JCal.cpp
Microsoft(R) C/C++ Optimizing Compiler Version 19.24.28314
Microsoft (R) .For .NET Framework version 4.08.4220.0
Copyright (C) Microsoft Corporation. All rights reserved.
JCal.cpp
Microsoft (R) Incremental Linker Version 14.24.28314.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:JCal.exe
JCal.obj
d:\Dev>JCal.exe 2019 4 30
Year: 2019/04/30
Japanese Calendar:Heisei 0031/04/30
Lunar calendar:Heisei 0031/03/26
Zodiac:Yin Earth Pig
Rokuyo:Buddha extinction
d:\Dev>JCal.exe 2019 5 1
Year: 2019/05/01
Japanese Calendar:Reiwa 0001/05/01
Lunar calendar:Reiwa 0001/03/27
Zodiac:Yin Earth Pig
Rokuyo:Daan
D language, C ++ / CLI wrapping source code
The preparation has become long, but the main subject is from here.
This is an implementation example of C ++ / CLI
wrapping process for calling .NET API
.
It is a simple function that sets the acquisition result of the lunar calendar and the Japanese calendar to struct JCAL
.
The ʻextern “C” __declspec (dllexport) `attribute is added so that it can be called from ** D language **.
JCalDll.cpp
using namespace System;
using namespace System::Globalization;
typedef struct {
int era;
int year;
int month;
int day;
int leapMonth;
int zodiac;
int kanshi;
int chishi;
int rokuyo;
} JCAL;
#define VC_DLL_EXPORTS extern "C" __declspec(dllexport)
VC_DLL_EXPORTS void __cdecl getJapaneseCalendar(int year, int month, int day, JCAL &jcal)
{
DateTime newDate = DateTime::DateTime(year, month, day);
JapaneseCalendar^ jc = gcnew JapaneseCalendar();
jcal.era = jc->GetEra(newDate);
jcal.year = jc->GetYear(newDate);
jcal.month = jc->GetMonth(newDate);
jcal.day = jc->GetDayOfMonth(newDate);
}
VC_DLL_EXPORTS void __cdecl getJapaneseLunisolarCalendar(int year, int month, int day, JCAL &jcal)
{
DateTime newDate = DateTime::DateTime(year, month, day);
JapaneseLunisolarCalendar^ jlc = gcnew JapaneseLunisolarCalendar();
jcal.era = jlc->GetEra(newDate);
jcal.year = jlc->GetYear(newDate);
jcal.month = jlc->GetMonth(newDate);
jcal.day = jlc->GetDayOfMonth(newDate);
jcal.leapMonth = 0;
//Get leap month
if ( jcal.year > 0 ){
int leapMonth = jlc->GetLeapMonth(jcal.year, jcal.era);
if ( jcal.month == leapMonth ){
jcal.leapMonth = 1;
}
//Correct the month when including leap months
if ( (leapMonth > 0) && (jcal.month >= leapMonth) ){
jcal.month = jcal.month - 1; //Lunar month correction
}
}
//Zodiac (Heavenly Stems, Zodiac)
jcal.zodiac = jlc->GetSexagenaryYear(newDate);
jcal.kanshi = jlc->GetCelestialStem(jcal.zodiac);
jcal.chishi = jlc->GetTerrestrialBranch(jcal.zodiac);
//Rokuyo(Daan, Akaguchi, First victory, Tomobiki, First defeat, Buddha extinction)
// (Month+Day) %6 remainder
jcal.rokuyo = (jcal.month + jcal.day) % 6;
}
Next, it is an implementation example of a program that calls C ++ / CLI
wrapping in ** D language ** and displays the lunar calendar and Japanese calendar.
The information you want to get is defined in the struct, the memory is secured, and it is passed to C ++ / CLI
.
Just by writing pragma (lib," JCalDll ")
and ʻextern (Windows) ~, you can call the function on the
C ++ / CLI` side, so it was easier to implement than I expected.
oldcal.d
import std.algorithm;
import std.conv;
import std.datetime;
import std.format;
import std.range;
import std.stdio;
import core.sys.windows.windows;
struct JCAL {
int era;
int year;
int month;
int day;
int leapMonth;
int zodiac;
int kanshi;
int chishi;
int rokuyo;
}
pragma(lib, "JCalDll");
extern (Windows) nothrow @nogc {
void getJapaneseCalendar(int, int, int, ref JCAL);
void getJapaneseLunisolarCalendar(int, int, int, ref JCAL);
}
string[] sEra =
[ "", "Meiji", "Taisho", "Showa", "Heisei", "Reiwa" ];
string[] sKanshi =
[ "", "Instep", "B", "Hinoe", "Ding", "戊", "Self", "Gung", "Spicy", "壬", "癸" ];
string[] sChishi =
[ "", "Child", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Not yet", "Monkey", "Rooster", "Dog", "亥" ];
string[] sRokuyo =
[ "Big", "Red", "Win", "friend", "negative", "Buddha" ];
// [ "Daan", "Red mouth", "First win", "Tomobiki", "Predecessor", "Buddha extinction" ];
void main(string[] args)
{
Date dt = Clock.currTime().to!Date;
if ( args.length > 2 ){
dt = Date(args[1].to!int, args[2].to!int, 1);
} else {
dt = Date(dt.year, dt.month, 1);
}
writef("\n%4d years%2d month", dt.year, dt.month);
JCAL jcal;
string line1 = " |".cycle.take(dt.dayOfWeek * 6).to!string;
string line2 = " |".cycle.take(dt.dayOfWeek * 7).to!string;
with ( jcal ){
getJapaneseCalendar(dt.year, dt.month, 1, jcal);
writef("(%s %2d years) ", sEra[era], year);
getJapaneseLunisolarCalendar(dt.year, dt.month, 1, jcal);
writefln("[%s%s]", sKanshi[kanshi], sChishi[chishi]);
writefln("%-( %s |%)", ["Day", "Month", "fire", "water", "wood", "Money", "soil" ]);
for ( int d = 1; d <= dt.daysInMonth; d++ ){
getJapaneseLunisolarCalendar(dt.year, dt.month, d, jcal);
line1 ~= format("%2d %s |", d, sRokuyo[rokuyo]);
line2 ~= format("%2d/%2d%s|", month, day,(leapMonth == 1) ? "*" : " ");
}
}
int num = (cast(int)line2.length / (7 * 7)) + 1;
string line0 = "-".cycle.take(num * 7 * 7).to!string;
roundRobin(line0.chunks(7 * 7), line1.chunks(6 * 7), line2.chunks(7 * 7))
.each!(s => writefln("%s", s));
}
Supplementary explanation of D language source code
If there are no parameters at runtime, it will be processed in today’s year and month. You can also specify the year and month with the ʻargs` parameter.
string line1
contains one double-byte character for each date. Therefore, the daily display width is 6
characters.
There are a total of 6
characters in the order of 2 characters for the date of the new calendar, 1 character for half-width space, 1 character for Rokuyo display or full-width space, 1 character for half-width space, and 1 character for |
.
string line2
is all half-width characters, and the daily display width is 7
characters.
There are 5 characters in the lunar year and month, 1 character in *
for half-width spaces or leap months, and 1 character in |
, for a total of 7
characters.
string line0
is the horizontal line-
.
Use chunks
to break line0 line1 line2
every 7 days and roundRobin
to alternate.
chunks
roundRobin
compile
Launch the x64 Native Tools Command Prompt for VS2019 (https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019).
Add the options / clr
and / LD
when compiling C ++ / CLI
. JCalDll.lib
and JCalDll.dll
are generated.
JCalDll.lib
is required when compiling D language sources. JCalDll.dll
is required when running ʻoldcal.exe`.
When compiling with dmd
, add the option -m64
to generate 64-bit code.
If you have ldc2
installed, you can use ldc2 oldcal.d
.
compile
d:\Dev>cl /clr /LD JCalDll.cpp
Microsoft(R) C/C++ Optimizing Compiler Version 19.24.28314
Microsoft (R) .For .NET Framework version 4.08.4220.0
Copyright (C) Microsoft Corporation. All rights reserved.
JCalDll.cpp
Microsoft (R) Incremental Linker Version 14.24.28314.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:JCalDll.dll
/dll
/implib:JCalDll.lib
JCalDll.obj
Library JCalDll.lib and object JCalDll.Creating exp
d:\Dev>dmd -m64 oldcal.d
Execution result
The displayed Japanese character code is ʻUTF-8, so please execute
chcp 65001 first.
If you want to output in Japanese with
Shift-JIS`, you need to add character code conversion processing to the source code as introduced before.
Introduction information 1
Introduction information 2
At the command prompt in my environment, I use Cica font.
Therefore, in the execution result, the double-byte space is displayed as a square frame.
Now you can see the lunar calendar of your birth month.
However, in the specification of GetLeapMonth
of the JapaneseLunisolarCalendar class, January 1960 AD If you try to get a leap month before 27 (1959 in the lunar calendar), you will get a ʻException`, so you can only view the lunar calendar after February 1960. The upper limit is December 2049 AD.
Reference information used this time
JapaneseCalendar class
JapaneseLunisolarCalendar class
Handle to Object Operator (^) (C ++ / CLI and C ++ / CX)
ref new, gcnew (C ++ / CLI and C ++ / CX)
[How to call C # methods from C ++]
(https://qiita.com/tetsurom/items/a0ad9bd24dbe513afdc4)
[Wrap C # with C ++ / CLI and call it from the C ++ app]
(https://qiita.com/tera1707/items/69bdfe99733b84c97217)
Rokuyo / Moon Age / Lunar Calendar
Year / Month / Day Zodiac
How is Rokuyo decided? How to calculate Rokuyo?
Calculation of Rokuyo with c #
Information that is not used this time but may be helpful
Introduction to C ++ / CLI Wrapping
[Try calling a C # DLL from a C ++ module]
(https://knowledge.rinpress.com/index.php/%EF%BC%A3%EF%BC%8B%EF%BC%8B%E3%81%AE%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%8B%E3%82%89%EF%BC%A3%EF%BC%83%E3%81%AE%EF%BC%A4%EF%BC%AC%EF%BC%AC%E3%82%92%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)
[How to use C # DLL directly from C ++]
(https://qiita.com/Midoliy/items/58d56e202f104ebf867a)
[Calling-NET-from-D]
(https://github.com/taylorh140/Calling-NET-from-D)