Monthly Archives: March 2014

Inversion of Control and Dependency Injection (advanced, Part 1), Programming Patterns

I finally had some time and spent some hours on Inversion of Control (IoC) and Dependency Injection (DI). When I did the same a few weeks ago I did not understand a lot. I got lost on the concept of inversion. I tried to figure out, what the inversion was. This blocked all and I ended up pretty much blank despite nearly an hour of pumping information into my head. When you get lost then you should do it at least properly and at full steam šŸ˜‰  Well, I finally understood the headline … and the world started making sense again.

 

Why the name ā€œInversion of Controlā€? (IoC)

An entirely hardcoded class controls the program execution path. All branches are predetermined. By using interfaces you can decouple theses flows, so that at the time of creation the class does not exactly know what instructions to call next.
Letā€™s say you are using events. The class, which is raising an event, hands the control over to another class. The subscribing class is in control of the program flow, not the class that raises the event. The subscribing class can subscribe or unsubscribe. The class, which is providing the event, should not have any active control over subscriptions. Hence it is an inversion of control.
By using IoC classes become more encapsulated. Letā€™s say a perfect class is blind and urgently needs a guide dog. The control can now be taken over by external factors. This inverts the control entirely.
Unit testing uses that mechanism. A container can be used to control classes and change the bindings.

 

What is a container?

The expression ā€œcontainerā€ is rarely used in C#. The C++ world calls containers what C# calls collections. We have come across containers in my C++ posts. Follow the link for a quick refresher.
To simplify the matter we can call a UnityContainer a dictionary of objects with some additional methods.
This container performs binding between components. For instance it replaces all specific interface declarations by fully instantiated classes without the need to explicitly call any initialization.

 

What is dependency?

The structure of a program is: Input, Calculations, Output. The same generally applies to classes. Letā€™s say you want to run an analysis of a text file. That analysis class can only function properly if the required text file does exist. The calculations depend on the input. The same applies to the output, which can only run if the calculation was successful.

The input class calls the calculation class, which in turn calls the output class. As we are discussing inversion, letā€™s decouple the classes and implement events. In this case the output class subscribes to the calculation result event and the calculator subscribes to the input event.

 

What Dependency Injection? (DI)

“Dependency Injection” is a subset of “Inversion of Control”. IoC is a principle, whereas DI is an actual implementation of the principle.
DI is a software design pattern that allows removing hard-coded dependencies. First of all you do not create objects directly; you just describe how they look like. To accomplish this, the pattern uses interfaces or base classes. As the dependency is not hardcoded, the actual object can vary each time. It just needs to fulfill the inheritable pattern of an interface or base class.

public class Car { }   // base class
public class Audi : Car { }
public class BMW : Car { }
public class Exhibition {
   Car StageObject { set; get; }  // assign Audi or BMW as the dependency object of class Exhibition
}

 

Interfaces

What are the benefits of interfaces besides multiple inheritance? Several people can work on different problems simultaneously by using interfaces. One person for instance writes the code for logging to text files and another person writes the code for logging to databases. If both use the same interface definition, then the underlying classes can be easily replaced by each other. Interfaces are like promises to provide predefined class patterns at runtime.
Thus we end up with component separation, which is very useful in unit testing. Interfaces can eliminate unfathomable dependencies if used wisely.

 

Dependency Injection and Unit Testing

When you run unit tests, then you will need input data. But it could be too complex to run the entire program just to test some methods. You would try to only instantiate the minimum requirements. Think of a syringe and inject the data into the test case to create an acceptable environment. This can be difficult in case the program was not structured well. You need to examine the code to find dependencies.

Inversion of Control

 

I will cover some IoC/DI basics today and will follow-up on this after some other posts, which were queuing up in the last weeks:

  • Calling Java from C#
  • Calling C# from Java
  • Implement all Java source code examples of the 15 day series C# to C++
  • WPF Datagrid formatting
  • Google Authenticator
  • create a post index for my blog

 

Dependency Injection

There are several ways to implement dependencies in a class. The easiest way is to have a field that holds a reference to the dependency, which is probably the worst approach you can have in terms of flexibility.

public class Report1 {
 private IDataBase _DB = new DataBase();
} 

Report1 r1 = new Report1(); // dependency DataBase must be figured out by examining the code

 

You can use methods or properties to tell your classes what dependency objects they should use. The best approach though is via constructors. You can hardly miss parameters when trying to call the constructor. Of course bad constructor overloading can jeopardize this concept.

public class Car { }  // base class (instead of an interface)
public class Audi : Car { }
public class BMW : Car { }
public class Exhibition {
   Car StageObject { set; get; }  // assign Audi or BMW as the dependency object of class Exhibition
}

public class Report2 {
   public IDataBase DB { get; private set; }
   public void SetDB(IDataBase xDB) { DB = xDB; }
} 

public class Report3 {
  public IDataBase DB { get; set; }
} 

public class Report4 {
   private IDataBase _DB;
   public Report4(IDataBase xDB) { _DB = xDB; }
} 

DataBase lDB = new DataBase();
Report2 r2 = new Report2(); r2.SetDB(lDB);
Report3 r3 = new Report3(); r3.DB = lDB;
Report4 r4 = new Report4(lDB); 

 
If the world was just like class Report4, then we could more or less end the post here. Unfortunately dependencies are often not that obvious. They are well hidden and you need to read the code thoroughly to build unit tests.
Dependency Injection goes further and the real take off takes place with Moq, which I will explain in the follow-up post.

The following code example was didactically compiled. You don’t need any further information, it should be self-explanatory. You can download unity by following this link or using NuGet.

using System;
using Microsoft.Practices.Unity;

public interface IDataBase {
   void QuerySomething();
} // interface

public interface ITextFile {
   void LoadSomething();
} // interface

public interface INetwork {
   string Text { set; get; }
   void ReceiveSomething();
} // interface

public class Network : INetwork {
   public void ReceiveSomething() { Console.WriteLine("Receiving TCP data ..."); }
   public string Text { set; get; }
} // class

public class DataBase : IDataBase {
   private string _Dummy = "I am doing something.";
   public void QuerySomething() { Console.WriteLine(_Dummy); }
} // class

public class TextFile1 : ITextFile {
   public void LoadSomething() { Console.WriteLine("TF1: Loading something..."); }
} // class

public class TextFile2 : ITextFile {
   public void LoadSomething() { Console.WriteLine("TF2: Loading something..."); }
} // class

public class TextFile3 : ITextFile {
   public void LoadSomething() { Console.WriteLine("TF3: Loading something..."); }
} // class

public class Report5 {
   public string Text = "#N/A";
   private IDataBase _DB;
   public readonly ITextFile TF;
   public IDataBase getDB() { return _DB; }
   public Report5(IDataBase xDB, ITextFile xTextFile) { _DB = xDB; TF = xTextFile; }
} // class

public class Report6 {
   public readonly string Text1;
   public readonly string Text2;
   private readonly ITextFile _TextFile;
   public readonly INetwork Network;
   public Report6(ITextFile xTextFile, INetwork xNetwork, string xText1, string xText2) {
      _TextFile = xTextFile;
      Network = xNetwork;
      Text1 = xText1;
      Text2 = xText2;
   } // constructor
} // class

class Program {

   static void Main(string[] args) {

      UnityContainer lContainer = new UnityContainer(); // using Microsoft.Practices.Unity;  

      Report5 r;

      // insufficient data
      Console.WriteLine("test: insufficient data");
      Console.WriteLine("Registering IDataBase");
      lContainer.RegisterType(typeof(IDataBase), typeof(DataBase)); // whenever someone asks for an IDataBase, then return a new DataBase instance    
      try {
         r = lContainer.Resolve<Report5>(); // throws an exception, because ITextFile is undefined
      }
      catch (Exception ex) { Console.WriteLine(ex.Message); }
      Console.WriteLine();

      // full data
      Console.WriteLine("test: sufficient data");
      Console.WriteLine("Registering ITextFile TF1");
      Console.WriteLine("IDataBase is still registered");
      lContainer.RegisterType(typeof(ITextFile), typeof(TextFile1));
      r = lContainer.Resolve<Report5>();
      Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile1
      r.getDB().QuerySomething();
      r.TF.LoadSomething(); // this is TextFile1
      Console.WriteLine();

      // override a previous type registration with another type
      Console.WriteLine("test: override a previous type registration with another type");
      Console.WriteLine("Registering ITextFile TF2");
      lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2)); // override the first type registration    
      //lContainer.RegisterType<ITextFile, TextFile2>();   // same as lContainer.RegisterType(typeof(ITextFile), typeof(TextFile2));
      r = lContainer.Resolve<Report5>();
      Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile2
      r.getDB().QuerySomething();
      r.TF.LoadSomething(); // this is TextFile2 
      Console.WriteLine();

      // override a previous type registration with an instance
      Console.WriteLine("test: override a previous type registration with an instance");
      Console.WriteLine("Registering an instance of TextFile3");
      ITextFile lTextFile = new TextFile3();
      lContainer.RegisterInstance(lTextFile);
      r = lContainer.Resolve<Report5>();
      Console.WriteLine("type of r.TF is " + r.TF.GetType()); // TextFile3
      r.getDB().QuerySomething();
      r.TF.LoadSomething(); // this is TextFile3 
      Console.WriteLine();

      // using names to register instances
      Console.WriteLine("test: using names to register instances");
      lContainer.RegisterType<Report5, Report5>(); // creates a default class without any name
      Report5 a = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("A", a); a.Text = "Report A";
      Report5 b = new Report5(r.getDB(), r.TF); lContainer.RegisterInstance("B", b); b.Text = "Report B";

      r = lContainer.Resolve<Report5>("A"); Console.WriteLine("got " + r.Text);
      r = lContainer.Resolve<Report5>("B"); Console.WriteLine("got " + r.Text);
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?";
      r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text);  // new instance
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // new instance
      Console.WriteLine();

      // => HAVE A LOOK AT THE BELOW CONTAINER SNAPSHOT => there are 3 instances for Report5

      // using names to register instances
      Console.WriteLine("test: revision, using the same instance");
      Console.WriteLine("ContainerControlledLifetimeManager: re-use instances (singleton behaviour for objects)");
      lContainer.RegisterType<Report5, Report5>(new ContainerControlledLifetimeManager());
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); r.Text = "same instance?";
      r = lContainer.Resolve<Report5>("X"); Console.WriteLine("got " + r.Text);  // new instance
      r = lContainer.Resolve<Report5>(); Console.WriteLine("got " + r.Text); // same instance !!!!!
      Console.WriteLine();

      // constructors with parameters
      lContainer.RegisterType<INetwork, Network>();
      lContainer.RegisterType<ITextFile, TextFile2>();
      Console.WriteLine("test: constructors with parameters");
      ResolverOverride[] lParameters = new ResolverOverride[] { new ParameterOverride("xText1", "Hello "), new ParameterOverride("xText2", "world") };
      Report6 lReport6 = lContainer.Resolve<Report6>(lParameters);
      Console.WriteLine("Report6 text field values are: " + lReport6.Text1 + lReport6.Text2);

      Console.ReadLine();
   } //

} // class

 

instances
 

example output:
test: insufficient data
Registering IDataBase
Resolution of the dependency failed, type = “Report5”, name = “(none)”.
Exception occurred while: while resolving.
Exception is: InvalidOperationException – The type ITextFile does not have an ac
cessible constructor.
———————————————–
At the time of the exception, the container was:

Resolving Report5,(none)
Resolving parameter “xTextFile” of constructor Report5(IDataBase xDB, ITextFil
e xTextFile)
Resolving ITextFile,(none)

test: sufficient data
Registering ITextFile TF1
IDataBase is still registered
type of r.TF is TextFile1
I am doing something.
TF1: Loading something…

test: override a previous type registration with another type
Registering ITextFile TF2
type of r.TF is TextFile2
I am doing something.
TF2: Loading something…

test: override a previous type registration with an instance
Registering an instance of TextFile3
type of r.TF is TextFile3
I am doing something.
TF3: Loading something…

test: using names to register instances
got Report A
got Report B
got #N/A
got #N/A
got #N/A

test: revision, using the same instance
ContainerControlledLifetimeManager: re-use instances (singleton behaviour for ob
jects)
got #N/A
got #N/A
got same instance?

test: constructors with parameters
Report6 text field values are: Hello world

migration Java, C++ (day 15), professional), call C++ from Java, call Java from C++

logo

Guys, day15! I was not giving up. I haven’t found any example on the web that explains, how to return arrays of classes/structures when crossing language barriers. I was close to giving up. But after roughly six hours I had found the solution. And as usual: In hindsight it all looks simple.

Today is the last day I cover C++ in this series. There will be more, but not as a part of this 15 day bet. I have learnt a lot during these days. I proved someone else’s opinion wrong and can firmly support people with a similar attitude now. Well, so the conclusion and good news is: YES, you can learn C++ in 15 days and be on a good level afterwards. All you need is resilience.
Surely, 15 days are not enough to become an expert. But it was amazing to see how much someone can learn in such a short time.

EDIT 27 March:
I spoke to a friend yesterday. He asked me why I was not using SWIG. It is useful to learn driving a car with gears before you upgrade to automatic transmission. However, here is the link for further studies to Swig.

What settings do we need today?

In C++ add jvm.lib to Additional Dependencies of the Linker. Then enter C:\Program Files\Java\jdk1.8.0\lib in field Additional Library Directories.

Setting1

You also need additional include directories:
a) C:\Program Files\Java\jdk1.8.0\include\win32
b) C:\Program Files\Java\jdk1.8.0\include

Setting2

Google for javah for further research. (You don’t need it here as I provide all required code). You can use javah to generate header files for you. It simplifies your work a lot. Furthermore you may be interested in the memory management of the JNI. You don’t need to manage objects, which you generate via the JNI in C++, yourself. Great deal!

DOS

I created a temporary directory in C:\temp . It helped me to keep my environment neat and tidy. Some people prefer to store the files directly in the build directories. But this can cause problems when you clear all project data to rebuild your code from scratch.

JNI sometimes uses weird abbreviations for IDs like in lJNIEnv->GetMethodID(lTestClass, “getFriends”, “()[LFriend;”). They look worse than they are. In the brackets () you enter the input parameter types. Next to it, on the right side, you have the output parameter type. Do not add any space or separators. For instance (II)V would describe a method with two integer parameters and a void return type.

JvmTypeSignatures

Calling Java code from C++

import javax.swing.JOptionPane;

public class TestLib {

  double[]  DoubleArrayField  = { 1.1, 2.2, 3.3, 4.4, 5.5 };
  int       IntField           = 99;

  public TestLib() {
    System.out.println("Java: Executing constructor");
  } // constructor

  public static void main(String[] args) {
    System.out.println("Java: main()");
  } //

  public String ShowErrorMessage(String xMessage) {
    System.out.println("Java: Do you see the nice Java MessgeBox?");
    int dialogResult = JOptionPane.showConfirmDialog(null, "Therefore let's use Javax Swing and do the stuff that would take too much time in C++.", xMessage, JOptionPane.YES_NO_CANCEL_OPTION);
    switch (dialogResult) {
      case JOptionPane.CANCEL_OPTION:
        return "Cancel? Any other decision would have been better than that one!";
      case JOptionPane.YES_OPTION:
        return "Yes? PS:I lov ya!";
      case JOptionPane.NO_OPTION:
        return "No? What a pessimist you are!";
      default:
    }

    return "impossible";
  } //

  public Friend[] getFriends() {
    Friend[] lFriends = new Friend[6];

    lFriends[0] = new Friend(1946, "Michael Sylvester Gardenzio Stallone");
    lFriends[1] = new Friend(1978, "Rocky Balboa");
    lFriends[2] = new Friend(1982, "John Rambo");
    lFriends[3] = new Friend(2013, "Ray Breslin");
    lFriends[4] = new Friend(1993, "Gabe Walker");
    lFriends[5] = new Friend(1989, "Ray Tango");

    return lFriends;
  } //

} // class
#include <iostream>
#include <jni.h>
#include <string>

using namespace std;


JNIEnv* GetJVM(JavaVM ** xJVM) {
  JavaVMOption lOptions;
  lOptions.optionString = "-Djava.class.path=C:\\Ohta\\cppin15days\\Day15\\bin"; // path to your java source code

  JavaVMInitArgs lInitArgs;
  lInitArgs.version = JNI_VERSION_1_6;
  lInitArgs.nOptions = 1;
  lInitArgs.options = &lOptions;
  lInitArgs.ignoreUnrecognized = 0;

  JNIEnv *lJNIEnv;
  int lResult = JNI_CreateJavaVM(xJVM, (void**)&lJNIEnv, &lInitArgs);
  if (lResult < 0)  cout << "Cannot launch JVM" << endl;

  return lJNIEnv;
} //


void test() {
  JavaVM *lJavaVM;
  JNIEnv *lJNIEnv = GetJVM(&lJavaVM);
  if (lJNIEnv == nullptr)  return;

  jclass lTestClass = lJNIEnv->FindClass("TestLib");
  if (!lTestClass) { std::cerr << "C++: TestLib class not found." << std::endl; return; }

  // call main()
  jmethodID lMain = lJNIEnv->GetStaticMethodID(lTestClass, "main", "([Ljava/lang/String;)V");
  if (!lMain) { std::cerr << "C++: Failed to get main()." << std::endl; return; }
  jobjectArray lArguments = lJNIEnv->NewObjectArray(1, lJNIEnv->FindClass("java/lang/String"), NULL);
  jstring lFirstArgument = lJNIEnv->NewStringUTF("dummy");
  lJNIEnv->SetObjectArrayElement(lArguments, 0, lFirstArgument);
  lJNIEnv->CallStaticVoidMethod(lTestClass, lMain, lArguments);

  // instantiate our TestClass
  jmethodID lConstructor = lJNIEnv->GetMethodID(lTestClass, "<init>", "()V"); // get the constructor by using <init>
  jobject lInstance = lJNIEnv->NewObject(lTestClass, lConstructor);

  // DoubleArrayField
  jobject lJArray = lJNIEnv->GetObjectField(lInstance, lJNIEnv->GetFieldID(lTestClass, "DoubleArrayField", "[D"));
  jdoubleArray *lJDoubles = reinterpret_cast<jdoubleArray*>(&lJArray);
  jsize lJSize = lJNIEnv->GetArrayLength(*lJDoubles);
  jboolean lFalse(false);
  double *lDoubles = lJNIEnv->GetDoubleArrayElements(*lJDoubles, &lFalse);
  cout << "DoubleArrayField: ";
  for (int i = 0; i < lJSize; i++) cout << " " << lDoubles[i];
  cout << endl;

  // IntField
  jint lInt = lJNIEnv->GetIntField(lInstance, lJNIEnv->GetFieldID(lTestClass, "IntField", "I"));
  cout << "IntField: " << (int)lInt << endl;

  // ShowErrorMessage()  
  jboolean lTrue(true);
  if (lJNIEnv->IsInstanceOf(lInstance, lTestClass) == JNI_TRUE) { // <= this code line is just for demo purposes
    jmethodID lMethod = lJNIEnv->GetMethodID(lTestClass, "ShowErrorMessage", "(Ljava/lang/String;)Ljava/lang/String;");
    lFirstArgument = lJNIEnv->NewStringUTF("A kiss from C++");
    jobject lResult = lJNIEnv->CallObjectMethod(lInstance, lMethod, lFirstArgument); // arguments in function call ,CallCharMethodA => arguments in a jvalues array, CallCharMethodV => arguments in a va_list   
    jstring lString = reinterpret_cast<jstring>(lResult);
    const char *s = lJNIEnv->GetStringUTFChars(lString, nullptr);
    cout << "C++: Java said => " << s << endl;   
    //lJNIEnv->ReleaseStringUTFChars(lString, s);
  }
  
  // getFriends() 
  jmethodID lMethod = lJNIEnv->GetMethodID(lTestClass, "getFriends", "()[LFriend;");
  jobject lResult = lJNIEnv->CallObjectMethod(lInstance, lMethod);  
  jobjectArray lArray = reinterpret_cast<jobjectArray>(lResult);
  jclass lFriendClass = lJNIEnv->FindClass("Friend");  
  
  int n = (int)lJNIEnv->GetArrayLength(lArray);
  for (int i = 0; i < n; i++)  {
    jobject lFriend = lJNIEnv->GetObjectArrayElement(lArray, jsize(i));
    jfieldID lFieldId0 = lJNIEnv->GetFieldID(lFriendClass, "friendSince", "I");
    int lFriendSince = lJNIEnv->GetIntField(lFriend, lFieldId0);
    jfieldID lFieldId1 = lJNIEnv->GetFieldID(lFriendClass, "name", "Ljava/lang/String;");
    jobject h = lJNIEnv->GetObjectField(lFriend, lFieldId1);   
    jstring lName = reinterpret_cast<jstring>(h);
    const char *s = lJNIEnv->GetStringUTFChars(lName, nullptr);  
    cout << s << " is my best buddy since " << lFriendSince << endl;
  }

  cin.get();
} //

example output:
Java: main()
Java: Executing constructor
DoubleArrayField: 1.1 2.2 3.3 4.4 5.5
IntField: 99
Java: Do you see the nice Java MessgeBox?
C++: Java said => No? What a pessimist you are!
Michael Sylvester Gardenzio Stallone is my best buddy since 1946
Rocky Balboa is my best buddy since 1978
John Rambo is my best buddy since 1982
Ray Breslin is my best buddy since 2013
Gabe Walker is my best buddy since 1993
Ray Tango is my best buddy since 1989

Calling C++ code from Java


// -------------------------------------------------------------------------------------------------------------
//   Friend.java 
// -------------------------------------------------------------------------------------------------------------

public class Friend {
  public int		friendSince;	// value type
  public String	name;				// reference type

  public Friend(int xFriendSince, String xName) {
    friendSince = xFriendSince;
    name = xName;
  }
} // class

// -------------------------------------------------------------------------------------------------------------
//   TestProgram.java file
// -------------------------------------------------------------------------------------------------------------

import java.io.IOException;

public class TestProgram {

  public native String ShowErrorMessage(String xMessage);
  public native Friend[] getFriends();

  public static void main(String[] args) throws IOException {
    System.load("c:\\temp\\Day15.dll"); 
    TestProgram lTestProgram = new TestProgram();
    String s = lTestProgram.ShowErrorMessage("Java: Hello C++");
    System.out.println(s);

    Friend[] lFriends = lTestProgram.getFriends();

    for (Friend lFriend : lFriends) {
      System.out.println(lFriend.name + " is my best buddy since " + lFriend.friendSince);
    }
    System.in.read();
  } //
} // class
// -------------------------------------------------------------------------------------------------------------
//   TestProgram.h  (generated with javah.exe)
// -------------------------------------------------------------------------------------------------------------

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestProgram */

#ifndef _Included_TestProgram
#define _Included_TestProgram
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestProgram
 * Method:    ShowErrorMessage
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_TestProgram_ShowErrorMessage (JNIEnv *, jobject, jstring);

/*
 * Class:     TestProgram
 * Method:    getFriends
 * Signature: ()[LFriend;
 */
JNIEXPORT jobjectArray JNICALL Java_TestProgram_getFriends (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

// -------------------------------------------------------------------------------------------------------------
//  Day15.cpp file
// -------------------------------------------------------------------------------------------------------------

#include <stdio.h>
#include <iostream>
#include <string>
#include <jni.h>
#include "C:\temp\TestProgram.h"

using namespace std;

jstring Alloc(JNIEnv *xJNIEnv, const char *xText){
  return xJNIEnv->NewStringUTF(xText);
} //

JNIEXPORT jstring JNICALL Java_TestProgram_ShowErrorMessage(JNIEnv *xJNIEnv, jobject xObject, jstring xMessage) {
  const char *lMessage = xJNIEnv->GetStringUTFChars(xMessage, nullptr);
  cout << "C++: I am too lazy for MessageBoxes." << endl;
  wcout << "C++: Your message was: " << lMessage << endl;
  return Alloc(xJNIEnv, "C++: Good night!");
} //

void AddFriend(JNIEnv *xJNIEnv, jclass xFriendClass, jobjectArray xArray, int xIndex, int xFriendSince, const char *xFriendName) {
  jobjectArray lArguments = xJNIEnv->NewObjectArray(2, xFriendClass, NULL);
  jint lFriendSince(xFriendSince);
  jstring lName = xJNIEnv->NewStringUTF(xFriendName);
  jmethodID lConstructor = xJNIEnv->GetMethodID(xFriendClass, "<init>", "(ILjava/lang/String;)V"); // get the constructor by using <init>
  jobject lInstance = xJNIEnv->NewObject(xFriendClass, lConstructor, lFriendSince, lName);
  xJNIEnv->SetObjectArrayElement(xArray, jsize(xIndex), lInstance);
} //

JNIEXPORT jobjectArray JNICALL Java_TestProgram_getFriends(JNIEnv *xJNIEnv, jobject xObject) {
  //jclass lObjectClass = xJNIEnv->GetObjectClass(xObject);      
  jclass lFriendClass = xJNIEnv->FindClass("Friend");
  jobjectArray lArray = xJNIEnv->NewObjectArray(6, lFriendClass, NULL);
  if (!lFriendClass) { std::cerr << "C++: Friend class not found." << std::endl; return lArray; }

  AddFriend(xJNIEnv, lFriendClass, lArray, 0, 1946, "Michael Sylvester Gardenzio Stallone");
  AddFriend(xJNIEnv, lFriendClass, lArray, 1, 1978, "Rocky Balboa");
  AddFriend(xJNIEnv, lFriendClass, lArray, 2, 1982, "John Rambo");
  AddFriend(xJNIEnv, lFriendClass, lArray, 3, 2013, "Ray Breslin");
  AddFriend(xJNIEnv, lFriendClass, lArray, 4, 1993, "Gabe Walker");
  AddFriend(xJNIEnv, lFriendClass, lArray, 5, 1989, "Ray Tango");

  return lArray;
} //

example output:
C++: I am too lazy for MessageBoxes.
C++: Your message was: Java: Hello C++
C++: Good night!
Michael Sylvester Gardenzio Stallone is my best buddy since 1946
Rocky Balboa is my best buddy since 1978
John Rambo is my best buddy since 1982
Ray Breslin is my best buddy since 2013
Gabe Walker is my best buddy since 1993
Ray Tango is my best buddy since 1989

migration C#, C++ (day 14), call C# from C++ (advanced), call C++ from C# (hardcore)

logo

Hardcore today.The second example is C# even though it looks like C++. So don’t get confused. Day 15 is nigh.

C++ is about speed and not comfort. Nevertheless, sometimes you want comfort. And it can be done. The C++ programmer can in fact call C# code from C++ to access the entire .Net Framework indirectly.
Usually you would use a TCP connection to achieve this. Unfortunately it does require work. And you sometimes do not have the source code of a C# library to quickly build in a network connection. And remoting is not a solution as well; the protocol needs to be the same. So you could end up in a lot of unnecessary programming. If you can, access available C# libraries directly. And today I show you how it can be done.

In case you need to write the library, make sure to amend the com-visibility in your AssemblyInfo.cs to [assembly: ComVisible(true)]. You also have to check the “Register for COM Interop” setting.

ComVisible

Oh, and before I forget this important detail. Start your Visual Studio in admin mode by right clicking it and then selecting “Run as administrator”. You won’t have enough user rights to register the library for com otherwise. In case you are not using Visual Studio, or you have to roll out the library on a different PC, then use RegAsm to register the library on that system. You may have a 32 bit and a 64 bit version of RegAsm on your PC. Use the right one to avoid nasty error messages.

Calling C# code from C++

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace TestLib {

  // struct is a value type
  [StructLayout(LayoutKind.Sequential)]
  public struct Friend {
    public int friendSince; // value type
    [MarshalAs(UnmanagedType.BStr)]
    public string name;  // reference type
  } // struct

  public interface IFriends {
    Friend[] GetFriends { get; }
    string ShowErrorMessage(string xMessage);
  } // interface

  public class Friends : IFriends {

    public string ShowErrorMessage(string xMessage) {
      Console.WriteLine("Do you see the nice .Net MessgeBox?");
      DialogResult lButtons = MessageBox.Show("Therefore let's use Windows Forms and do the stuff that would take too much time in C++.", xMessage, MessageBoxButtons.YesNoCancel);
      switch (lButtons) {
        case DialogResult.Cancel:
          return "Cancel? Any other decision would have been better than that one!";
        case DialogResult.No:
          return "No? What a pessimist you are!";
        case DialogResult.Yes:
          return "Yes? PS:I lov ya!";
        default:
          return "impossible";
      }
    } //


    public Friend[] GetFriends {
      get {
        Friend[] lFriends = new Friend[6];
        lFriends[0].name = "Michael Sylvester Gardenzio Stallone";
        lFriends[0].friendSince = 1946;

        lFriends[1].name = "Rocky Balboa";
        lFriends[1].friendSince = 1978;

        lFriends[2].name = "John Rambo";
        lFriends[2].friendSince = 1982;

        lFriends[3].name = "Ray Breslin";
        lFriends[3].friendSince = 2013;

        lFriends[4].name = "Gabe Walker";
        lFriends[4].friendSince = 1993;

        lFriends[5].name = "Ray Tango";
        lFriends[5].friendSince = 1989;

        return lFriends;
      }
    } //

  } // class
} // namespace
#include <windows.h>
#include <stdio.h>
#include <iostream>
#pragma warning (disable: 4278)
#import <mscorlib.tlb> // import the CLR
#import "..\TestLib\bin\Debug\TestLib.tlb"  // path to your C# library

using namespace std;
using namespace TestLib;

void test() {
  HRESULT lHResult;

  lHResult = CoInitialize(NULL); // Initializes the COM library on the current thread and identifies the concurrency model as single-thread apartment (STA).
  if (FAILED(lHResult)) { cout << "Cannot initialize com." << endl; return; }

  IFriends *lFriends = nullptr;
  lHResult = CoCreateInstance(__uuidof(Friends), NULL, CLSCTX_INPROC_SERVER, __uuidof(IFriends), (void**)&lFriends);
  if (FAILED(lHResult)) { cout << "Instance creation failed" << endl; return; }

  Friend HUGEP *lBSTR;
  SAFEARRAY *lSafeArray = lFriends->GetFriends;
  lHResult = SafeArrayAccessData(lSafeArray, (void HUGEP* FAR*)&lBSTR);
  if (FAILED(lHResult)) { cout << "Getting a property failed" << endl; return; }
  for (ULONG i = 0; i < lSafeArray->rgsabound->cElements; i++) {
    wcout << lBSTR[i].name << " is my best buddy since " << lBSTR[i].friendSince << endl;  // 64bit: lBSTR[0].name is wchar_t (Unicode)
  }
  SafeArrayUnaccessData(lSafeArray);

  _bstr_t lMessage("C++ is not happy enough with Rambo!");
  _bstr_t lReply = lFriends->ShowErrorMessage(lMessage);
  wcout << "C# replied: " << lReply << endl; // .GetBSTR()

  CoUninitialize();
  cin.get();
} //

example output:
Michael Sylvester Gardenzio Stallone is my best buddy since 1946
Rocky Balboa is my best buddy since 1978
John Rambo is my best buddy since 1982
Ray Breslin is my best buddy since 2013
Gabe Walker is my best buddy since 1993
Ray Tango is my best buddy since 1989
Do you see the nice .Net MessgeBox?
C# replied: Yes? PS:I lov ya!

MsgBox

Let’s do the opposite now. C# coders sometimes need to access mashine code (C++, Assembler, etc).
I decided to make it a bit more difficult today. As mentioned already: It is day 14. Therefore I thought about returning an array of a structure from C++ to C#. I was checking the internet and after more than an hour I still had not found anything that could be used. C# most likely does not support it.
After a while I got the idea to use pointers in C#. They are very unusual and only for professional people. Well, finally I found a practical example for something seemingly useless. You can really crash your PC if you do it wrong. The critical code is marked with the keyword unsafe:

unsafe {
  Friend* lFriend = (Friend*)lIntPtr;
  for (int i = 0, n = FriendsCount(); i < n; i++) {
    char* lChars = (char*)lFriend->name;
    string lWho = new string(lChars);
    Console.WriteLine("C#: since " + lFriend->friendSince + " my best buddy is " + lWho);
    lFriend++;
  }
}

I have to admit, in hindsight it does look easy. But it took a lot of time to get there. Check the internet. There is no solution out there on how to read returned arrays … at least I did not find it. Many people have failed it seems.
First of all you have to enable unsafe code to use pointers in C#. Go to your Build settings and check the Allow unsafe code option:

Unsafe

The unknown size with arrays of structures can be solved by using pointers in the structure rather than the content itself. This way the size is equidistant. Surely, you cannot solve the problem when you have no source code access to the library and the returned structure size is variable. I see no practical com-example for this. Indeed all programmers of all languages would face the same problem here. C++ does not tell you the size, because there is no meta-data. This is why I implemented a function to get the array size separately. I am sure you have used buffers in the past. You always have to oversize them. This is a similar problem, not the same though.

Calling C++ code from C#

This really is C#. It can use pointers.

using System;
using System.Runtime.InteropServices;

namespace Day14b {
  class Program {

    [StructLayout(LayoutKind.Sequential)]
    public struct Friend {
      public int friendSince;
      public IntPtr name;
    }; // struct

    private const string cLibrary = @"C:\Users\Username\Desktop\Day14b\Debug\TestLib.dll";

    [DllImport(cLibrary, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.BStr)]
    static extern string ShowErrorMessage(string xMessage);

    [DllImport(cLibrary, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr GetFriend();

    [DllImport(cLibrary)]
    static extern int FriendsCount();

    [DllImport(cLibrary)]
    static extern void ReleaseMemory();

    static void Main(string[] args) {
      string lFeedback = ShowErrorMessage("C#: Hello C++");
      Console.WriteLine("C#: C++ replied -> " + lFeedback + Environment.NewLine);

      Console.WriteLine("C#: Number of friends: " + FriendsCount());
      Console.WriteLine("C#: Fasten your seatbelts! This is hardcore C#");

      IntPtr lIntPtr = GetFriend();
      Console.WriteLine();
      Console.WriteLine("C#: Number of friends: " + FriendsCount());
      Console.WriteLine();
      unsafe {
        Friend* lFriend = (Friend*)lIntPtr;
        for (int i = 0, n = FriendsCount(); i < n; i++) {
          char* lChars = (char*)lFriend->name;
          string lWho = new string(lChars);
          Console.WriteLine("C#: since " + lFriend->friendSince + " my best buddy is " + lWho);
          lFriend++; // increment the pointer, it now points to the next structure in the array
        }
      }

      ReleaseMemory();
      Console.ReadKey();
    } //

  } // class
} // namespace
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
#include <windows.h>

using namespace std;

extern "C" {
  struct Friend {
    int friendSince;
    const wchar_t *name;
  }; // struct

  vector<wchar_t *> _BStrings;
  Friend *_Friends = nullptr;

  const wchar_t *Alloc(const wchar_t *xText){
    wchar_t * lBSTR = SysAllocString(xText);
    _BStrings.push_back(lBSTR);
    return lBSTR;
  } //

  __declspec(dllexport) const wchar_t* ShowErrorMessage(BSTR xMessage) {
    cout << "C++: I am too lazy for MessageBoxes." << endl;
    wcout << "C++: Your message was: " << xMessage << endl;
    return Alloc(L"C++: Good night!");
  } //

  __declspec(dllexport) int FriendsCount() {
    if (_Friends == nullptr) return 0;
    return 6; // which is a constant value in our example
  } //

  __declspec(dllexport) Friend *GetFriend(int xIndexer) {
    if (_Friends != nullptr) return _Friends;

    _Friends = new Friend[6];
    _Friends[0].name = Alloc(L"Michael Sylvester Gardenzio Stallone");
    _Friends[0].friendSince = 1946;

    _Friends[1].name = Alloc(L"Rocky Balboa");
    _Friends[1].friendSince = 1978;

    _Friends[2].name = Alloc(L"John Rambo");
    _Friends[2].friendSince = 1982;

    _Friends[3].name = Alloc(L"Ray Breslin");
    _Friends[3].friendSince = 2013;

    _Friends[4].name = Alloc(L"Gabe Walker");
    _Friends[4].friendSince = 1993;

    _Friends[5].name = Alloc(L"Ray Tango");
    _Friends[5].friendSince = 1989;

    return _Friends;  // be aware of the possible memory leak, you have to free lFriend
  } //

  __declspec(dllexport) void ReleaseMemory()  {
    for (auto &lBSTR : _BStrings) SysFreeString(lBSTR);
    if (_Friends != nullptr) delete[] _Friends;
    cout << endl << "C++: Memory released." << endl;
  } //

} // extern

example output:
C++: I am too lazy for MessageBoxes.
C++: Your message was: C#: Hello C++
C#: C++ replied -> C++: Good night!

C#: Number of friends: 0
C#: Fasten your seatbelts! This is hardcore C#

C#: Number of friends: 6

C#: since 1946 my best buddy is Michael Sylvester Gardenzio Stallone
C#: since 1978 my best buddy is Rocky Balboa
C#: since 1982 my best buddy is John Rambo
C#: since 2013 my best buddy is Ray Breslin
C#: since 1993 my best buddy is Gabe Walker
C#: since 1989 my best buddy is Ray Tango

C++: Memory released.

migration C#, Java, C++ (day 13), HashTable

The one who has not used a Dictionary in C# is definitely not a programmer. You need Dictionaries to lookup values quickly. But how do they work?
Dictionaries use HashTables. These are, to simplify it here, large arrays of linked lists.

HashTable

To demonstrate the technique I chose small arrays of 5 elements in my example source codes. Otherwise it would be difficult to produce duplicate entries, because hash values are supposed to have as few duplicates as possible.

Let’s say you have 6 objects and an algorithm that produces equally distributed values between 0 and 7 (see picture) for each object. Now you do not have to walk through a list anymore to find the objects. Simply use that hash value (0 to 7) to check, which of the 8 lists you have to look at. The number of search steps would ideally be only 1/8 compared to a list. Unfortunately hash values are not unique. Therefore you can have many entries for one hash value. In my examples I use a simple linked list to store them.

The C# Dictionary class is obviously more complex and uses larger arrays and nested search algorithms. Well, the following simplified examples are easier to grasp.

HashTables

using System;

namespace Day13b {
  class Program {

    public class HashTable<K, V> {
      public class Entry {
        public K key;
        public V value;
        public Entry next = null;

        public Entry(K xKey, V xValue) { key = xKey; value = xValue; }
      }  // class

      private readonly uint _HashTableSize;
      private readonly Entry[] _HashTable;

      public HashTable(uint xHashTableSize) {
        _HashTableSize = xHashTableSize;
        _HashTable = new Entry[xHashTableSize];
        for (int i = 0; i < xHashTableSize; i++) _HashTable[i] = null;
      } // constructor

      public void Print() {
        for (int i = 0; i < _HashTableSize; i++) {
          if (_HashTable[i] == null) continue;
          Entry lEntry = _HashTable[i];
          while (lEntry != null) {
            Entry lNext = lEntry.next;
            Console.WriteLine("HashTable[" + i + "] has key " + lEntry.key);
            lEntry = lNext;
          }
        }
      } //

      public uint GetHashCode(string xKey) {
        uint lHash = 2166136261;  // initial FNV www.isthe.com/chongo/tech/comp/fnv
        for (int i = 0; i < xKey.Length; i++) {
          lHash = lHash ^ (xKey[i]);
          lHash = lHash * 16777619;  // FNVMultiple
        }
        return lHash % _HashTableSize;
      } //

      public uint GetHashCode(uint xKey) {
        return xKey % _HashTableSize;
      } //

      public uint GetHashCode(K xKey) {
        if (xKey is string) return GetHashCode(xKey as string);
        if (xKey is uint) return GetHashCode(Convert.ToUInt32(xKey));
        return (uint)xKey.GetHashCode() % _HashTableSize; // general approach         
      } //



      public V AddOrReplace(K xKey, V xValue) {
        uint lHash = GetHashCode(xKey);
        Entry lEntry = _HashTable[lHash];

        // new entry
        if (lEntry == null) {
          _HashTable[lHash] = new Entry(xKey, xValue);
          return default(V);
        }

        while (lEntry != null) {
          if (lEntry.key.Equals(xKey)) {
            // replace existing entry  
            V lRemove = lEntry.value;
            lEntry.value = xValue;
            Console.WriteLine("Replacing " + lRemove);
            return lRemove;
          }
          if (lEntry.next == null) {
            // end of linked list
            lEntry.next = new Entry(xKey, xValue);
            return default(V);
          }
          lEntry = lEntry.next;
        }

        // non reachable code:
        return default(V);
      } //

      public V TryGetValue(K xKey) {
        uint lHash = GetHashCode(xKey);
        if (_HashTable[lHash] == null) return default(V);

        Entry lEntry = _HashTable[lHash];
        while ((lEntry != null) && (!lEntry.key.Equals(xKey))) lEntry = lEntry.next;

        if (lEntry == null) return default(V);
        else return lEntry.value;
      } //

      public V Remove(K xKey) {
        uint lHash = GetHashCode(xKey);
        Entry lEntry = _HashTable[lHash];
        if (lEntry == null) return default(V);

        Entry lPrevious = null;
        while (true) {
          if (lEntry == null) return default(V); // end of linked list
          if (lEntry.key.Equals(xKey)) {
            // remove existing entry  
            V lRemove = lEntry.value;
            if (lPrevious != null) lPrevious.next = lEntry.next;
            else _HashTable[lHash] = null; // no previous element
            return lRemove;
          }
          lPrevious = lEntry;
          lEntry = lEntry.next;
        }

        // non reachable code
        //return default(V);  // the compiler complains properly šŸ™‚
      } //

    }; // class


    public class Data {
      public int id;
      public string text;

      public override string ToString() {
        return "id: " + id + ", text: " + text;
      } //

      ~Data() {
        Console.WriteLine("good bye Data struct with id " + id + " and text " + text);
      } // destructor

    } // class


    static void Main(string[] args) {
      // test with KEY string and VALUE struct

      HashTable<string, Data> lHashTable = new HashTable<string, Data>(5);
      string[] lTestStrings = { "hello", "world", "hola", "mundo", "hallo", "Welt", "hej", "verden", "hello", "wereld" };
      int i = 0;
      foreach (string s in lTestStrings) {
        Console.WriteLine("HashCode example: " + s + " = " + lHashTable.GetHashCode(s));
        Data lData = new Data();
        lData.text = s;
        lData.id = i++;
        lHashTable.AddOrReplace(s, lData);
      }
      lHashTable.Print();
      Data lRemoved = lHashTable.Remove("hej");
      lRemoved = null;  // de-reference for garbage collector (just for demo purposes)
      lHashTable.Print();
      Data a = lHashTable.TryGetValue("hej");
      Data b = lHashTable.TryGetValue("hallo");
      if (a == null) Console.WriteLine("a is null");
      if (b != null) Console.WriteLine("b has been found");

      Console.WriteLine();
      Console.WriteLine();

      // test with KEY integer and VALUE integer

      HashTable<uint, uint> lHashTable2 = new HashTable<uint, uint>(5);
      uint[] lTestIntegers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
      foreach (uint x in lTestIntegers) {
        Console.WriteLine("HashCode example: " + x + " = " + lHashTable2.GetHashCode(x));
        lHashTable2.AddOrReplace(x, x + 100);
      }
      lHashTable2.Print();

      GC.Collect();   // calls destructors of de-referenced objects
      Console.ReadKey();
    } //

  } // class
} // namespace

example output:
HashCode example: hello = 3
HashCode example: world = 2
HashCode example: hola = 3
HashCode example: mundo = 0
HashCode example: hallo = 1
HashCode example: Welt = 2
HashCode example: hej = 0
HashCode example: verden = 0
HashCode example: hello = 3
Replacing id: 0, text: hello
HashCode example: wereld = 1
HashTable[0] has key mundo
HashTable[0] has key hej
HashTable[0] has key verden
HashTable[1] has key hallo
HashTable[1] has key wereld
HashTable[2] has key world
HashTable[2] has key Welt
HashTable[3] has key hello
HashTable[3] has key hola
HashTable[0] has key mundo
HashTable[0] has key verden
HashTable[1] has key hallo
HashTable[1] has key wereld
HashTable[2] has key world
HashTable[2] has key Welt
HashTable[3] has key hello
HashTable[3] has key hola
a is null
b has been found

HashCode example: 1 = 1
HashCode example: 2 = 2
HashCode example: 3 = 3
HashCode example: 4 = 4
HashCode example: 5 = 0
HashCode example: 6 = 1
HashCode example: 7 = 2
HashCode example: 8 = 3
HashCode example: 9 = 4
HashCode example: 10 = 0
HashCode example: 11 = 1
HashCode example: 12 = 2
HashTable[0] has key 5
HashTable[0] has key 10
HashTable[1] has key 1
HashTable[1] has key 6
HashTable[1] has key 11
HashTable[2] has key 2
HashTable[2] has key 7
HashTable[2] has key 12
HashTable[3] has key 3
HashTable[3] has key 8
HashTable[4] has key 4
HashTable[4] has key 9
good bye Data struct with id 0 and text hello
good bye Data struct with id 6 and text hej

One of the main differences between C# and C++ is the memory management. I call the garbage collector at the end with GC.Collect() to show that some objects were de-referenced during the runtime.

Let me also highlight one part of the C# code: GetHashCode(K xKey) {}.
Although there is a definition for string, the above method is called. In fact we have a duplicate overload as soon as we define K as string or unsigned integer. The reason is that the generic class is not specific enough. The type K is closer to K than string. It therefore has a higher priority.

public V AddOrReplace(K xKey, V xValue) {
  uint lHash = GetHashCode(xKey);
  ....

And now compare it with a clear type declaration.

foreach (uint x in lTestIntegers) {
  ....
  lHashTable2.AddOrReplace(x, x + 100);
}

C++ frees memory as soon as you call delete. Compare the output. The destructors are called immediately. You could also use a kind of smart memory management in C++ as well. But why would you then use C++ at all? Such memory management indirectly questions the need for pure C++. The garbage collector for C# is highly optimized. If you need memory management, then don’t program in C++.

#include <string>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstdlib>

using namespace std;

template <typename K, typename V>
class HashTable {
public:
  class Entry {
  public:
    K key;
    V value;
    Entry *next;

    Entry(K xKey, V xValue)  { key = xKey; value = xValue;    next = nullptr; }
  }; // class

private:
  int _HashTableSize;
  Entry **_HashTable;

public:

  HashTable(int xHashTableSize) : _HashTableSize(xHashTableSize) {
    _HashTable = new Entry*[xHashTableSize];
    for (int i = 0; i < xHashTableSize; i++)  _HashTable[i] = nullptr;
  } // constructor

  ~HashTable() {
    for (int i = 0; i < _HashTableSize; i++) {
      if (_HashTable[i] == nullptr) continue;
      Entry *lThis = _HashTable[i];
      while (lThis != nullptr) {
        if (lThis->next == nullptr) break;
        Entry *lNext = lThis->next;
        delete lThis;
        lThis = lNext;
      }
    }
    delete[] _HashTable;
  } // destructor

  void Print() {
    for (int i = 0; i < _HashTableSize; i++) {
      if (_HashTable[i] == nullptr) continue;
      Entry *lEntry = _HashTable[i];
      while (lEntry != nullptr) {
        Entry *lNext = lEntry->next;
        cout << "HashTable[" << i << "] has key " << lEntry->key << endl;
        lEntry = lNext;
      }
    }
  } //

  int GetHashCode(const string &xKey) {
    size_t lHash = 2166136261U; // initial FNV www.isthe.com/chongo/tech/comp/fnv
    for (size_t i = 0; i < xKey.length(); i++) {
      lHash = lHash ^ (xKey[i]);
      lHash = lHash * 16777619;  // FNVMultiple
    }
    return lHash % _HashTableSize;
  } //

  int GetHashCode(int xKey) {
    return xKey % _HashTableSize;
  } //



  V AddOrReplace(K xKey, V xValue)  {
    int lHash = GetHashCode(xKey);
    Entry *lEntry = _HashTable[lHash];

    // new entry
    if (lEntry == nullptr)  {
      _HashTable[lHash] = new Entry(xKey, xValue);
      return V();
    }

    while (lEntry != nullptr) {
      if (lEntry->key == xKey) {
        // replace existing entry  
        V lRemove = lEntry->value;
        lEntry->value = xValue;
        return lRemove;
      }
      if (lEntry->next == nullptr) {
        // end of linked list
        lEntry->next = new Entry(xKey, xValue);
        return V();
      }
      lEntry = lEntry->next;
    }

    // non reachable code:
    return V();
  } //

  V TryGetValue(K xKey) {
    int lHash = GetHashCode(xKey);
    if (_HashTable[lHash] == nullptr) return V();

    Entry *lEntry = _HashTable[lHash];
    while ((lEntry != nullptr) && (lEntry->key != xKey)) lEntry = lEntry->next;

    if (lEntry == nullptr) return V();
    else return lEntry->value;
  } //

  V Remove(K xKey) {
    int lHash = GetHashCode(xKey);
    Entry *lEntry = _HashTable[lHash];
    if (lEntry == nullptr) return V();

    Entry *lPrevious = nullptr;
    while (true) {
      if (lEntry == nullptr) return V(); // end of linked list
      if (lEntry->key == xKey) {
        // remove existing entry  
        V lRemove = lEntry->value;
        if (lPrevious != nullptr) lPrevious->next = lEntry->next;
        else _HashTable[lHash] = nullptr; // no previous element
        delete lEntry;
        return lRemove;
      }
      lPrevious = lEntry;
      lEntry = lEntry->next;
    }

    // non reachable code
    return V();
  } //

}; // class

struct Data {
  int id;
  string text;
  ~Data() {
    cout << "good bye Data struct with id " << id << " and text " << text << endl;
  }
}; // struct


void test() {

  // test with KEY string and VALUE struct

  HashTable<string, Data*> *lHashTable = new HashTable<string, Data*>(5);
  string lTestStrings[] = { "hello", "world", "hola", "mundo", "hallo", "Welt", "hej", "verden", "hello", "wereld" };
  int i = 0;
  for each (string s in lTestStrings) {
    cout << "HashCode example: " << s << " = " << lHashTable->GetHashCode(s) << endl;
    Data *lData = new Data();
    lData->text = s;
    lData->id = i++;
    Data *lReplaced = lHashTable->AddOrReplace(s, lData);
    if (lReplaced != nullptr) delete lReplaced;
  }
  lHashTable->Print();
  Data *lRemoved = lHashTable->Remove("hej");
  if (lRemoved != nullptr) delete lRemoved;
  lHashTable->Print();
  Data *a = lHashTable->TryGetValue("hej");
  Data *b = lHashTable->TryGetValue("hallo");
  if (a == nullptr) cout << "a is null" << endl;
  if (b != nullptr) cout << "b has been found" << endl;

  delete lHashTable;

  cout << endl << endl;

  // test with KEY integer and VALUE integer

  HashTable<int, int> *lHashTable2 = new HashTable<int, int>(5);
  int lTestIntegers[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
  for each (int x in lTestIntegers) {
    cout << "HashCode example: " << x << " = " << lHashTable2->GetHashCode(x) << endl;
    lHashTable2->AddOrReplace(x, x + 100);
  }
  lHashTable2->Print();
  delete lHashTable2;

  cin.get();
} //

example output:
HashCode example: hello = 3
HashCode example: world = 2
HashCode example: hola = 3
HashCode example: mundo = 0
HashCode example: hallo = 1
HashCode example: Welt = 2
HashCode example: hej = 0
HashCode example: verden = 0
HashCode example: hello = 3
good bye Data struct with id 0 and text hello
HashCode example: wereld = 1
HashTable[0] has key mundo
HashTable[0] has key hej
HashTable[0] has key verden
HashTable[1] has key hallo
HashTable[1] has key wereld
HashTable[2] has key world
HashTable[2] has key Welt
HashTable[3] has key hello
HashTable[3] has key hola
good bye Data struct with id 6 and text hej
HashTable[0] has key mundo
HashTable[0] has key verden
HashTable[1] has key hallo
HashTable[1] has key wereld
HashTable[2] has key world
HashTable[2] has key Welt
HashTable[3] has key hello
HashTable[3] has key hola
a is null
b has been found

HashCode example: 1 = 1
HashCode example: 2 = 2
HashCode example: 3 = 3
HashCode example: 4 = 4
HashCode example: 5 = 0
HashCode example: 6 = 1
HashCode example: 7 = 2
HashCode example: 8 = 3
HashCode example: 9 = 4
HashCode example: 10 = 0
HashCode example: 11 = 1
HashCode example: 12 = 2
HashTable[0] has key 5
HashTable[0] has key 10
HashTable[1] has key 1
HashTable[1] has key 6
HashTable[1] has key 11
HashTable[2] has key 2
HashTable[2] has key 7
HashTable[2] has key 12
HashTable[3] has key 3
HashTable[3] has key 8
HashTable[4] has key 4
HashTable[4] has key 9

  public class Data {

    public int id;
    public String text;

    @Override
    public String toString() {
      return "id: " + id + ", text: " + text;
    }

    @Override
    protected void finalize() throws Throwable {
      try {
        System.out.println("good bye Data struct with id " + id + " and text " + text);
      } finally {
        super.finalize();
      }
    } // destructor

  } // class
//--------------------------------------------------------------------------------------------
import java.util.ArrayList;

public class HashTable<K, V> {

  public class Entry {

    public K key;
    public V value;
    public Entry next = null;

    public Entry(K xKey, V xValue) {
      key = xKey;
      value = xValue;
    } // constructor
  } // class

  private final long _HashTableSize;
  private final ArrayList<Entry> _HashTable;

  public HashTable(int xHashTableSize) {
    _HashTableSize = xHashTableSize;
    _HashTable = new ArrayList<Entry>();
    //_HashTable = new Entry[xHashTableSize]; // Java generic array creation error
    for (int i = 0; i < xHashTableSize; i++) _HashTable.add(null);
  } // constructor

  public final void Print() {
    for (int i = 0; i < _HashTableSize; i++) {
      if (_HashTable.get(i) == null) continue;
      Entry lEntry = _HashTable.get(i);
      while (lEntry != null) {
        Entry lNext = lEntry.next;
        System.out.println("HashTable[" + i + "] has key " + lEntry.key);
        lEntry = lNext;
      }
    }
  } //

  public final long hashCode(long xKey) {
    return xKey % _HashTableSize;
  } //

  public final long hashCode(K xKey) { 
    if (xKey instanceof Long) return hashCode((Long) xKey);
    return Math.abs(xKey.hashCode() % _HashTableSize); // general approach
  } //

  public final V AddOrReplace(K xKey, V xValue) {
    int lHash = (int) hashCode(xKey);
    Entry lEntry = _HashTable.get(lHash);

    // new entry
    if (lEntry == null) {
      _HashTable.set(lHash, new Entry(xKey, xValue));
      return null;
    }

    while (lEntry != null) {
      if (lEntry.key.equals(xKey)) {
        // replace existing entry 
        V lRemove = lEntry.value;
        lEntry.value = xValue;
        System.out.println("Replacing " + lRemove);
        return lRemove;
      }
      if (lEntry.next == null) {
        // end of linked list
        lEntry.next = new Entry(xKey, xValue);
        return null;
      }
      lEntry = lEntry.next;
    }

    // non reachable code:
    return null;
  } //

  public final V TryGetValue(K xKey) {
    int lHash = (int) hashCode(xKey);
    if (_HashTable.get(lHash) == null) return null;

    Entry lEntry = _HashTable.get(lHash);
    while ((lEntry != null) && (!lEntry.key.equals(xKey)))
      lEntry = lEntry.next;

    if (lEntry == null) return null;
    else return lEntry.value;
  } //

  public final V Remove(K xKey) {
    int lHash = (int) hashCode(xKey);
    Entry lEntry = _HashTable.get(lHash);
    if (lEntry == null) return null;

    Entry lPrevious = null;
    while (true) {
      if (lEntry == null) return null; // end of linked list          
      if (lEntry.key.equals(xKey)) {
        // remove existing entry 
        V lRemove = lEntry.value;
        if (lPrevious != null)
          lPrevious.next = lEntry.next;
        else // no previous element
          _HashTable.set(lHash, null);
        return lRemove;
      }
      lPrevious = lEntry;
      lEntry = lEntry.next;
    }

      // non reachable code
    //return null;
  } //
} // class
//--------------------------------------------------------------------------------------------

import java.io.IOException;

public class Program {

  public static void main(String[] args) {
    try {
      HashTable<String, Data> lHashTable = new HashTable<>(5);
      //= new HashTable<>(5);
      String[] lTestStrings = {"hello", "world", "hola", "mundo", "hallo", "Welt", "hej", "verden", "hello", "wereld"};
      int i = 0;
      for (String s : lTestStrings) {
        System.out.println("HashCode example: " + s + " = " + lHashTable.hashCode(s));
        Data lData = new Data();
        lData.text = s;
        lData.id = i++;
        lHashTable.AddOrReplace(s, lData);
      }
      lHashTable.Print();
      Data lRemoved = lHashTable.Remove("hej");
      lRemoved = null; // de-reference for garbage collector (just for demo purposes)
      lHashTable.Print();
      Data a = lHashTable.TryGetValue("hej");
      Data b = lHashTable.TryGetValue("hallo");
      if (a == null) System.out.println("a is null");
      if (b != null) System.out.println("b has been found");

      System.out.println();
      System.out.println();

      // test with KEY integer and VALUE integer
      HashTable<Integer, Integer> lHashTable2 = new HashTable<>(5);
      int[] lTestIntegers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
      for (int x : lTestIntegers) {
        System.out.println("HashCode example: " + x + " = " + lHashTable2.hashCode(x));
        lHashTable2.AddOrReplace(x, x + 100);
      }
      lHashTable2.Print();

      System.gc(); // calls destructors of de-referenced objects
      System.in.read();
    } catch (IOException ex) {
      System.out.println("IOException: " + ex.getMessage());
    }
  } //
  
} // class

example output:
HashCode example: hello = 2
HashCode example: world = 2
HashCode example: hola = 0
HashCode example: mundo = 2
HashCode example: hallo = 3
HashCode example: Welt = 2
HashCode example: hej = 1
HashCode example: verden = 2
HashCode example: hello = 2
Replacing id: 0, text: hello
HashCode example: wereld = 3
HashTable[0] has key hola
HashTable[1] has key hej
HashTable[2] has key hello
HashTable[2] has key world
HashTable[2] has key mundo
HashTable[2] has key Welt
HashTable[2] has key verden
HashTable[3] has key hallo
HashTable[3] has key wereld
HashTable[0] has key hola
HashTable[2] has key hello
HashTable[2] has key world
HashTable[2] has key mundo
HashTable[2] has key Welt
HashTable[2] has key verden
HashTable[3] has key hallo
HashTable[3] has key wereld
a is null
b has been found

HashCode example: 1 = 1
HashCode example: 2 = 2
HashCode example: 3 = 3
HashCode example: 4 = 4
HashCode example: 5 = 0
HashCode example: 6 = 1
HashCode example: 7 = 2
HashCode example: 8 = 3
HashCode example: 9 = 4
HashCode example: 10 = 0
HashCode example: 11 = 1
HashCode example: 12 = 2
HashTable[0] has key 5
HashTable[0] has key 10
HashTable[1] has key 1
HashTable[1] has key 6
HashTable[1] has key 11
HashTable[2] has key 2
HashTable[2] has key 7
HashTable[2] has key 12
HashTable[3] has key 3
HashTable[3] has key 8
HashTable[4] has key 4
HashTable[4] has key 9
good bye Data struct with id 0 and text hello
good bye Data struct with id 6 and text hej

migration C#, Java, C++ (day 12), binary tree

Trees are not difficult. They just require a little bit of practice in recursion. Today’s code uses the minimum framework. You can easily enhance this code for your purposes. Inherit from class Node or implement an interface in your personalized tree.
You have probably realized that I do not add new C++ fundamentals to learn in the C# to C++ series. The reason is that I do want to stay platform independent and also not use any non-standard classes.
Thus the code can be seen as pure training of C# to C++ translations.

tree as tree can

using System;

namespace Day12 {
  class Program {

    public class Node {
      public int key;
      public string description;
      public Node up = null;
      public Node down = null;
      public Node same = null;

      public Node(int xKey, string xDescription) { key = xKey; description = xDescription; }
    } // class


    public class BinaryTree {
      Node root;

      private void remove(Node xLeaf) {
        if (xLeaf == null) return;
        remove(xLeaf.same);
        remove(xLeaf.down);
        remove(xLeaf.up);
      } //

      private void insert(Node xNewLeaf, Node xLeaf) {
        // down
        if (xNewLeaf.key < xLeaf.key) {
          if (xLeaf.down != null) {
            insert(xNewLeaf, xLeaf.down);
            return;
          }
          xLeaf.down = xNewLeaf;
          return;
        }

        // up
        if (xNewLeaf.key > xLeaf.key) {
          if (xLeaf.up != null) {
            insert(xNewLeaf, xLeaf.up);
            return;
          }
          xLeaf.up = xNewLeaf;
          return;
        }

        // same
        if (xLeaf.same != null) insert(xNewLeaf, xLeaf.same);
        else xLeaf.same = xNewLeaf;
      } //

      public Node search(int xKey, Node xLeaf) {
        if (xLeaf != null) {
          if (xKey == xLeaf.key) return xLeaf;
          if (xKey < xLeaf.key) return search(xKey, xLeaf.down);
          else return search(xKey, xLeaf.up);
        }
        else return null;
      } //

      public void insert(Node xNewLeaf) {
        if (root != null) insert(xNewLeaf, root);
        else root = xNewLeaf;
      } //

      public Node search(int xKey) {
        return search(xKey, root);
      } //

      private void removeAll() {
        remove(root);
      } //

      public static void print(Node xLeaf, int xLevel, string xDirection) {
        if (xLeaf == null) return;

        for (int i = 0; i < xLevel; i++) { Console.Write(" "); }
        Console.WriteLine(xDirection + xLeaf.key + " " + xLeaf.description);
        xLevel++;
        print(xLeaf.same, xLevel, ".");
        print(xLeaf.down, xLevel, "-");
        print(xLeaf.up, xLevel, "+");
      } //

    } // class


    static void Main(string[] args) {
      BinaryTree lTree = new BinaryTree();
      Node lRoot = new Node(1975, "Despicable Me");
      lTree.insert(lRoot);
      lTree.insert(new Node(1926, "Elisabeth"));
      lTree.insert(new Node(1948, "Charles"));
      lTree.insert(new Node(1982, "William"));
      lTree.insert(new Node(1984, "Harry"));
      lTree.insert(new Node(1961, "Diana"));
      lTree.insert(new Node(1947, "Camilla"));
      lTree.insert(new Node(1921, "Phillip"));
      lTree.insert(new Node(1983, "Pippa"));
      lTree.insert(new Node(1982, "Catherine"));
      lTree.insert(new Node(1984, "George Orwell"));

      Node lSearchResult = lTree.search(1984);
      Console.WriteLine("found some nice guys in 1984: " + lSearchResult.description + " & " + lSearchResult.same.description + Environment.NewLine + Environment.NewLine);

      BinaryTree.print(lRoot, 0, "*");

      lTree = null;     // lTree.removeAll(); called by the garbage collector  

      Console.ReadKey();
    } //

  } // class
} // namespace

import java.io.IOException;

public class Program {

  public static class Node {

    public int key;
    public String description;
    public Node up = null;
    public Node down = null;
    public Node same = null;

    public Node(int xKey, String xDescription) {
      key = xKey;
      description = xDescription;
    } // constructor
  } // class

  public static class BinaryTree {

    private Node root;

    private void remove(Node xLeaf) {
      if (xLeaf == null)
        return;
      remove(xLeaf.same);
      remove(xLeaf.down);
      remove(xLeaf.up);
    } //

    private void insert(Node xNewLeaf, Node xLeaf) {
      // down
      if (xNewLeaf.key < xLeaf.key) {
        if (xLeaf.down != null) {
          insert(xNewLeaf, xLeaf.down);
          return;
        }
        xLeaf.down = xNewLeaf;
        return;
      } //

      // up
      if (xNewLeaf.key > xLeaf.key) {
        if (xLeaf.up != null) {
          insert(xNewLeaf, xLeaf.up);
          return;
        }
        xLeaf.up = xNewLeaf;
        return;
      } //

      // same
      if (xLeaf.same != null) insert(xNewLeaf, xLeaf.same);
      else xLeaf.same = xNewLeaf;
    } //

    public final Node search(int xKey, Node xLeaf) {
      if (xLeaf != null) {
        if (xKey == xLeaf.key)
          return xLeaf;
        if (xKey < xLeaf.key)
          return search(xKey, xLeaf.down);
        else
          return search(xKey, xLeaf.up);
      } else
        return null;
    } //

    public final void insert(Node xNewLeaf) {
      if (root != null)
        insert(xNewLeaf, root);
      else
        root = xNewLeaf;
    } // 

    public final Node search(int xKey) {
      return search(xKey, root);
    } //

    private void removeAll() {
      remove(root);
    } //

    public static void print(Node xLeaf, int xLevel, String xDirection) {
      if (xLeaf == null) return;

      for (int i = 0; i < xLevel; i++)
        System.out.print(" ");
      System.out.println(xDirection + xLeaf.key + " " + xLeaf.description);
      xLevel++;
      print(xLeaf.same, xLevel, ".");
      print(xLeaf.down, xLevel, "-");
      print(xLeaf.up, xLevel, "+");
    } //

  } // class

  public static void main(String[] args) {
    try {
      BinaryTree lTree = new BinaryTree();
      Node lRoot = new Node(1975, "Despicable Me");
      lTree.insert(lRoot);
      lTree.insert(new Node(1926, "Elisabeth"));
      lTree.insert(new Node(1948, "Charles"));
      lTree.insert(new Node(1982, "William"));
      lTree.insert(new Node(1984, "Harry"));
      lTree.insert(new Node(1961, "Diana"));
      lTree.insert(new Node(1947, "Camilla"));
      lTree.insert(new Node(1921, "Phillip"));
      lTree.insert(new Node(1983, "Pippa"));
      lTree.insert(new Node(1982, "Catherine"));
      lTree.insert(new Node(1984, "George Orwell"));

      Node lSearchResult = lTree.search(1984);
      System.out.println("found some nice guys in 1984: " + lSearchResult.description + " & " + lSearchResult.same.description + "\n\n");

      BinaryTree.print(lRoot, 0, "*");

      lTree = null; // lTree.removeAll(); called by the garbage collector

      System.in.read();
    } catch (IOException ex) {
      System.out.println("IOException: " + ex.getMessage());
    }
  } //

} // class
#include <functional>
#include <string>
#include <iostream>

using namespace std;


class Node {
public:
  int key;
  string description;
  Node *up = nullptr;
  Node *down = nullptr;
  Node *same = nullptr;

  Node(int xKey, const string &xDescription) { key = xKey; description = xDescription; }
}; // class


class BinaryTree {
public:
  BinaryTree() { root = nullptr; }
  ~BinaryTree() { removeAll(); }

  void insert(Node *xNewLeaf);
  Node *search(int xKey);
  static void print(Node *xLeaf, int xLevel, const string &xDirection);
  void removeAll();

private:
  void remove(Node *xLeaf);
  void insert(Node *xNewLeaf, Node *xLeaf);
  Node *search(int key, Node *xLeaf);

  Node *root;
}; // class

void BinaryTree::remove(Node *xLeaf) {
  if (xLeaf == nullptr) return;
  remove(xLeaf->same);
  remove(xLeaf->down);
  remove(xLeaf->up);
  delete xLeaf;
} //

void BinaryTree::insert(Node *xNewLeaf, Node *xLeaf) {
  // down
  if (xNewLeaf->key < xLeaf->key) {
    if (xLeaf->down != nullptr) {
      insert(xNewLeaf, xLeaf->down);
      return;
    }

    xLeaf->down = xNewLeaf;
    return;
  }

  // up
  if (xNewLeaf->key > xLeaf->key)  {
    if (xLeaf->up != nullptr) {
      insert(xNewLeaf, xLeaf->up);
      return;
    }

    xLeaf->up = xNewLeaf;
    return;
  }

  // same
  if (xLeaf->same != nullptr) insert(xNewLeaf, xLeaf->same);
  else xLeaf->same = xNewLeaf;
} //

Node *BinaryTree::search(int xKey, Node *xLeaf) {
  if (xLeaf != nullptr) {
    if (xKey == xLeaf->key) return xLeaf;
    if (xKey < xLeaf->key) return search(xKey, xLeaf->down);
    else return search(xKey, xLeaf->up);
  }
  else return nullptr;
} //

void BinaryTree::insert(Node *xNewLeaf) {
  if (root != nullptr) insert(xNewLeaf, root);
  else root = xNewLeaf;
} //

Node *BinaryTree::search(int xKey) {
  return search(xKey, root);
} //

void BinaryTree::removeAll() {
  remove(root);
} //

void BinaryTree::print(Node *xLeaf, int xLevel, const string &xDirection) {
  if (xLeaf == nullptr) return;
  
  for (int i = 0; i < xLevel; i++)  { cout << " "; }
  cout << xDirection << xLeaf->key << " " << xLeaf->description << endl;
  xLevel++;
  print(xLeaf->same, xLevel, ".");
  print(xLeaf->down, xLevel, "-");
  print(xLeaf->up, xLevel, "+");  
} //

void test() {

  BinaryTree lTree;
  Node *lRoot = new Node(1975, "Despicable Me");
  lTree.insert(lRoot);
  lTree.insert(new Node(1926, "Elisabeth"));
  lTree.insert(new Node(1948, "Charles"));
  lTree.insert(new Node(1982, "William"));
  lTree.insert(new Node(1984, "Harry"));
  lTree.insert(new Node(1961, "Diana"));
  lTree.insert(new Node(1947, "Camilla"));
  lTree.insert(new Node(1921, "Phillip"));
  lTree.insert(new Node(1983, "Pippa"));
  lTree.insert(new Node(1982, "Catherine"));
  lTree.insert(new Node(1984, "George Orwell"));

  Node *lSearchResult = lTree.search(1984);
  cout << "found some nice guys in 1984: " << lSearchResult->description << " & " << lSearchResult->same->description << endl;
  cout << endl << endl;

  lTree.print(lRoot, 0, "*");

  // lTree.removeAll();  called by the deconstructor
  cin.get();
} //
found some nice guys in 1984: Harry & George Orwell


*1975 Despicable Me
 -1926 Elisabeth
  -1921 Phillip
  +1948 Charles
   -1947 Camilla
   +1961 Diana
 +1982 William
  .1982 Catherine
  +1984 Harry
   .1984 George Orwell
   -1983 Pippa

migration C#, Java, C++ (day 11), future and promise

There is hardly anything directly to compare with in C#. Future and promise are C++ concepts, which are probably best compared to C# task concepts. The comparison was not easy today. Have a look at the outcome.

Wiki

future and promise

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Program {
    public class Day11 {

        private class Params {
            public int Input;
            public int Result;
        }

        private void DoSomething() {
            Console.WriteLine("DoSomething()");
            Thread.Sleep(4000);
        }  //     

        private void Method1(object xObject) {
            Params lParams = xObject as Params;
            Console.WriteLine("Method1");
            Thread.Sleep(2000);
            lParams.Result = 2 * lParams.Input;
        } //

        private int Method2(int xInt1) {
            Console.WriteLine("Method2");
            Thread.Sleep(2000);
            return 2 * xInt1;
        } //

        private async Task<int> Method3(int xInt1) {
            await Task.Delay(0);
            Console.WriteLine("Method3");
            return 2 * xInt1;
        } //

        private void Method4(Params xParams, AutoResetEvent xEvent) {
            Console.WriteLine("Method4");
            Thread.Sleep(2000);
            xEvent.WaitOne();
            xParams.Result = 2 * xParams.Input;
        } //


        static void Main(string[] args) {
            Day11 lDay10 = new Day11();
            lDay10.test();
        } //

        public async void test() {
            int i;

            // use a thread to get an asynchronous result
            ParameterizedThreadStart lParams = new ParameterizedThreadStart(Method1);
            Thread thread1 = new Thread(lParams);
            Params p = new Params() { Input = 123 };
            thread1.Start(p);
            Console.WriteLine("Method1.join()");
            thread1.Join();
            Console.WriteLine("Method1 result is: " + p.Result);

            Func<int, int> t1 = new Func<int, int>(x => { return Method2(x); });
            i = t1(123); // synchronous
            IAsyncResult lResult = t1.BeginInvoke(123, null, null); // asynchronously
            lResult.AsyncWaitHandle.WaitOne();

            i = await Method3(123);
            Console.WriteLine("Method3 result is: " + i);

            p.Input = 123;
            p.Result = 0;
            AutoResetEvent lEvent = new AutoResetEvent(false);
            Task t2 = new Task(() => Method4(p, lEvent));
            t2.Start();
            lEvent.Set();
            t2.Wait();
            Console.WriteLine("Method4 result is: " + p.Result);

            Console.ReadLine();
        } //

    } // class
} // namespace

example output:
Method1.join()
Method1
Method1 result is: 246
Method2
Method2
Method3
Method3 result is: 246
Method4
Method4 result is: 246

#include <future>
#include <iostream>
#include <thread>

using namespace std;

void DoSomething() {
  cout << "DoSomething()" << endl;
  this_thread::sleep_for(chrono::seconds(4));
}  //

void Thread1(int xInt1, int &xInt2) {
  cout << "Thread1" << endl;
  this_thread::sleep_for(chrono::seconds(2));
  xInt2 = 2 * xInt1;
} //

int Thread2(int xInt1) {
  cout << "Thread2" << endl;
  this_thread::sleep_for(chrono::seconds(2));
  return 2 * xInt1;
} //

int Thread3(future<int> &xInt1) {
  cout << "Thread3" << endl;
  this_thread::sleep_for(chrono::seconds(2));
  return 2 * xInt1.get(); // .get() => waits until the promise is fullfilled
} //

int Thread4(shared_future<int> xInt1) {
  cout << "Thread4" << endl;
  this_thread::sleep_for(chrono::seconds(2));
  return 2 * xInt1.get(); // returns the same value to all waiting futures
} //

void test(){
  int i;

  // use a thread to get an asynchronous result
  thread t1(Thread1, 123, ref(i));
  cout << "Thread1.join()" << endl;
  t1.join();
  cout << "Thread1 result is: " << i << endl;

  // like a task, may be synchronous or asychronous  
  future<int> f1 = async(Thread2, 123);
  cout << "Thread2, f1.get()" << endl;
  i = f1.get(); // waits

  // like a synchronous task, which is running on the same thread
  // will be called when you get the value
  future<int> f2 = async(launch::deferred, Thread2, 123); 
  cout << "Thread2, f2.get()" << endl; 
  i = f2.get(); // waits
  cout << "Thread2 deferred result is: " << i << endl;

  // like an asynchronous task, which is running on a new thread
  future<int> f3 = async(launch::async, Thread2, 123); 
  cout << "Thread2, f3.get()" << endl; 
  i = f3.get(); // waits
  cout << "Thread2 async result is: " << i << endl;

  // same as f1, because this is the default value; the system decides what to do
  future<int> f4 = async(launch::async | launch::deferred, Thread2, 123);
  i = f4.get(); // waits

  promise<int> lPromise5; // promise to provide a value at a later time  
  future<int> f5 = lPromise5.get_future();
  future<int> f5b = async(launch::async, Thread3, ref(f5));
  DoSomething();
  cout << "lPromise5.set_value()" << endl;
  lPromise5.set_value(123); // fulfill our promise now
  cout << "f5b async result is: " << f5b.get() << endl;

  promise<int> lPromise6; // promise to provide a value at a later time
  future<int> f6 = lPromise6.get_future();
  DoSomething();
  // tell the parallel thread to stop waiting for the promise fulfillment
  lPromise6.set_exception(make_exception_ptr(runtime_error("sorry, I cannot fulfill my promise")));  

  //
  // SOME PRACTICAL ISSUES
  //
  promise<int> lPromise7; // promise to provide a value at a later time
  future<int> f7 = lPromise7.get_future();
  promise<int> lPromise8 = move(lPromise7); // you cannot assign a promise, you have to move it
  future<int> f8 = move(f7); // same with the future    

  // you cannot call future.get() more than once
  // to allow multiple consumers use:
  shared_future<int> f9 = f8.share();
  future<int> f10 = async(launch::async, Thread4, f9);
  future<int> f11 = async(launch::async, Thread4, f9);
  future<int> f12 = async(launch::async, Thread4, f9);
  future<int> f13 = async(launch::async, Thread4, f9);
  lPromise8.set_value(123);
  cout << "f10: " << f10.get() << ", f11:" << f11.get() << ", f12:" << f12.get() << ", f13:" << f13.get() << endl;

  // packaged_task
  auto t2 = bind(Thread2, 123);
  t2(); // synchronous

  packaged_task<int()> t3(bind(Thread2, 123)); // wrapper to make async calls
  t3(); // call the task  in a different context
  future<int> f14 = t3.get_future(); // getting a future is not possible with t2 
  int x = f14.get();
  cout << "f14: " << x << endl;

  cin.get();
} //

example output:
Thread1.join()
Thread1

Thread1 result is: 246
Thread2, f1.get()
Thread2

Thread2, f2.get()
Thread2
Thread2 deferred result is: 246
Thread2, f3.get()
Thread2

Thread2 async result is: 246
Thread2
DoSomething()
Thread3

lPromise5.set_value()
f5b async result is: 246
DoSomething()
Thread4
Thread4

Thread4
Thread4
f10: 246, f11:246, f12:246, f13:246
Thread2
Thread2
f14: 246

package Program;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Day11 {

  private static void Sleep(int xMilliseconds) {
    try {
      Thread.sleep(xMilliseconds);
    } catch (InterruptedException ex) {
      System.err.println(ex.getMessage());
    }
  } //

  private class Thread1 extends Thread {

    public int Input;
    public int Result;

    @Override
    public void run() {
      System.out.println("Thread1");
      Sleep(2000);
      Result = 2 * Input;
    } //
  } // class

  private class Thread2 {

    private final ExecutorService pool = Executors.newFixedThreadPool(5);

    public Future<Integer> getPromise(final int xInt1) {
      return pool.submit(() -> {
        System.out.println("Thread2");
        Sleep(2000);
        return 2 * xInt1;
      });
    } //
  } // class

  private class Thread3 implements Callable<Integer> {

    private final ExecutorService _Pool = Executors.newFixedThreadPool(5);
    private int _Int1;

    public Future<Integer> getPromise(final int xInt1) {
      _Int1 = xInt1;
      return _Pool.submit(this);
    } //

    @Override
    public Integer call() throws Exception {
      synchronized (this) {
        this.wait();
      }
      System.out.println("Thread3");
      Sleep(2000);
      return 2 * _Int1;
    } //
  } // class

  public final void test() throws InterruptedException, ExecutionException {

    // use a thread to get an asynchronous result    
    Thread1 t1 = new Thread1();
    t1.Input = 123;
    t1.start();
    System.out.println("Thread1.join()");
    t1.join();
    System.out.println("Thread1 result is: " + t1.Result);

    // like a task, may be synchronous or asychronous 
    Thread2 t2 = new Thread2();
    Future<Integer> f1 = t2.getPromise(123);
    System.out.println("Thread2, f1.getPromise(123)");
    int i = f1.get(); // waits   
    System.out.println("f1 result is: " + i);

    // deferred
    Thread3 t3 = new Thread3();
    Future<Integer> f2 = t3.getPromise(123);
    synchronized (t3) {
      t3.notify(); // start calculation manually    
    }
    System.out.println("Thread3 result is: " + f2.get()); // wait

    new Scanner(System.in).nextLine();
  } //

  public static void main(String[] args) throws InterruptedException, ExecutionException {
    Day11 lDay11 = new Day11();
    lDay11.test();
  } //

} // class

example output:
Thread1
Thread1.join()
Thread1 result is: 246
Thread2
Thread2, f1.getPromise(123)
f1 result is: 246
Thread3
Thread3 result is: 246

migration C#, Java, C++ (day 10), chrono, time, exceptions

I have mentioned speed quite often. Let’s measure it now!

It is remarkable that C# is dealing with exceptions faster than C++. The code execution is amazing. Anyway, when it comes to thrown exceptions (rarely happens in practice), then C++ is more than 20x faster. I knew how to build in the exceptions to see a difference. I was astonished that C++ was beyond belief compared to C# thrown exceptions. For further study follow these links:
Exceptions part 1
Exceptions part 2
Exceptions part 3

Time, Benchmark and Co.

using System;
using System.Diagnostics;

namespace DemoApp {
   public class Benchmark {

      void Benchmark1() {
         for (int i = 0; i < 1000; i++) { Console.Write("*"); }
         Console.WriteLine();
      } //

      void Benchmark2() {
         for (int i = 0; i < 1000; i++) {
            try { Console.Write("*"); }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
         }
         Console.WriteLine();
      } //

      void Benchmark3() {
         for (int i = 0; i < 1000; i++) {
            try { Console.Write("*"); throw new Exception("OMG"); }
            catch (Exception ex) { }
         }
         Console.WriteLine();
      } //

      void Benchmark4b() { Console.Write("*"); throw new Exception("OMG"); }
      void Benchmark4a() {
         for (int i = 0; i < 1000; i++) {
            try { Benchmark4b(); }
            catch (Exception ex) { }
         }
         Console.WriteLine();
      } //

      public class StopWatch {
         Stopwatch _Stopwatch = new Stopwatch();

         public void start() { _Stopwatch.Start(); }
         public void stop() {
            _Stopwatch.Stop();
            long lElapsedTicks = _Stopwatch.ElapsedTicks;
            long lTicksPerSecond = Stopwatch.Frequency;
            double lMilliseconds = 1000.0 * (double)lElapsedTicks / (double)lTicksPerSecond;
            Console.WriteLine(lMilliseconds + " ms ");
            _Stopwatch.Start();
         }
      };


      public void test() {
         DateTime lNow1 = DateTime.Now; // current time
         DateTime lNow2 = DateTime.Now.AddSeconds(2.0); // current time plus 2 seconds
         double lSince1970 = DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;  // Seconds since 1 Jan 1970  

         Console.WriteLine("Current local time and date: " + DateTime.Now.ToString("dd MMM yyyy, HH:mm:ss"));
         Console.WriteLine("1/" + Stopwatch.Frequency);  // eg. 1/1,000,000,000 == 1ns

         // Benchmark
         DateTime lStart = DateTime.Now;
         Benchmark1();
         DateTime lEnd = DateTime.Now;
         TimeSpan lDuration = lEnd - lStart;
         Console.WriteLine("Time passed ms: " + lDuration.TotalMilliseconds);

         // Benchmarks: let's get more precise now
         StopWatch lStopwatch = new StopWatch();
         lStopwatch.start();
         Console.Write("Benchmark1:"); Benchmark1(); lStopwatch.stop();
         Console.Write("Benchmark2:"); Benchmark2(); lStopwatch.stop();
         Console.Write("Benchmark3:"); Benchmark3(); lStopwatch.stop();
         Console.Write("Benchmark4:"); Benchmark4a(); lStopwatch.stop();

         Console.ReadKey();
      } //

   } // class
} // namespace

example output:
Current local time and date: 12 Mar 2014, 21:35:46
1/3122812
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
****************************************
Time passed ms: 62.0035
Benchmark1:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
63.3714101265142 ms
Benchmark2:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
133.177405492229 ms
Benchmark3:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
6450.41872517462 ms
Benchmark4:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
13223.9343899024 ms

#include <iostream>
#include <chrono>
#include <time.h>
#include <string>

using namespace std;
using namespace std::chrono;

void Benchmark1() {
  for (int i = 0; i < 1000; i++) { cout << "*"; }
  cout << endl;
} //

void Benchmark2() {
  for (int i = 0; i < 1000; i++) {
    try { cout << "*"; }
    catch (exception &ex) { cout << ex.what() << endl; }
  }
  cout << endl;
} //

void Benchmark3() {
  for (int i = 0; i < 1000; i++) {
    try { cout << "*"; throw exception("OMG"); }
    catch (exception &ex) {}
  }
  cout << endl;
} //

void Benchmark4b(){ cout << "*"; throw exception("OMG"); }
void Benchmark4a() {
  for (int i = 0; i < 1000; i++) {
    try { Benchmark4b(); }
    catch (exception &ex) {}
  }
  cout << endl;
} //

class Stopwatch {
  high_resolution_clock _Clock;
  high_resolution_clock::time_point _From;

public:
  void start() { _From = _Clock.now(); }
  void stop() {
    high_resolution_clock::time_point lNow = _Clock.now();
    high_resolution_clock::duration lDuration = lNow - _From;
    intmax_t lNum = high_resolution_clock::period::num;
    intmax_t lDen = high_resolution_clock::period::den;
    double lMilliseconds = 1000.0 * lDuration.count() * (double)lNum / (double)lDen;
    cout << lMilliseconds << " ms " << endl;
    _From = _Clock.now();
  }
};


int main() {
  system_clock c1;  // system clock changes will have an impact
  steady_clock c2;  // independent from system clock changes
  high_resolution_clock c3;

  system_clock::time_point lTimePoint = system_clock::now();
  cout << lTimePoint.time_since_epoch().count() << endl;
  lTimePoint = lTimePoint + seconds(2);
  cout << lTimePoint.time_since_epoch().count() << endl;
  
  time_t lRawTime;
  struct tm lLocalTime;
  const int lTimeStringLength = 50;
  char lTimeString[lTimeStringLength];
  bool b1 = (time(&lRawTime) != -1);                 // Seconds since 1 Jan 1970
  bool b2 = (localtime_s(&lLocalTime, &lRawTime) == 0); // Convert to local time
  bool b3 = (asctime_s(lTimeString, lTimeStringLength, &lLocalTime) == 0); // convert to string

  if (b1 && b2 && b3) cout << "Current local time and date: " << lTimeString << endl;
  else cerr << "Error, cannot get current time." << endl;
    
  ratio<1, 10> r1; // 1/10
  ratio<2, 10> r2; // 2/10
  cout << r1.num << "/" << r1.den << endl;  // 1/10
  cout << r2.num << "/" << r2.den << endl;  // 1/5
  cout << system_clock::period::num << "/" << system_clock::period::den << endl;  // eg. 1/1,000,000,000 == 1ns
  
  microseconds micros(1234);       // 1,234 microseconds
  cout << micros.count() << endl;  // 1,234
  nanoseconds ns = micros;         // 1,234,000 nanoseconds, no loss
  cout << ns.count() << endl;      // 1,234,000
  milliseconds ms = duration_cast<milliseconds>(micros); // 1 millisecond, precision loss => duration_cast required
  micros += ms;
  cout << micros.count() << endl;  // 2,234

  // Benchmark
  steady_clock::time_point lStart = steady_clock::now();
  Benchmark1();
  steady_clock::time_point lEnd = steady_clock::now();
  steady_clock::duration lDuration = lEnd - lStart;
  if (lDuration == steady_clock::duration::zero()) cout << "no time has passed" << endl;
  cout << duration_cast<milliseconds>(lDuration).count() << endl;

  // Benchmarks: let's get more precise now
  Stopwatch lStopwatch;
  lStopwatch.start();
  cout << "Benchmark1:"; Benchmark1(); lStopwatch.stop();
  cout << "Benchmark2:"; Benchmark2(); lStopwatch.stop();
  cout << "Benchmark3:"; Benchmark3(); lStopwatch.stop();
  cout << "Benchmark4:"; Benchmark4a(); lStopwatch.stop();

  cin.get();
  return 0;
} //

example output:
13946584250282411
13946584270282411
Current local time and date: Wed Mar 12 21:07:05 2014

1/10
1/5
1/10000000
1234
1234000
2234
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
****************************************
80
Benchmark1:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
86.0049 ms
Benchmark2:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
84.0048 ms
Benchmark3:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
311.018 ms
Benchmark4:*********************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
***************************************************
306.017 ms

package DemoApp;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;

 public class Benchmark {

  private void Benchmark1() {
     for (int i = 0; i < 1000; i++) System.out.print("*");
     System.out.println();
  } //

  private void Benchmark2() {
    for (int i = 0; i < 1000; i++) {
      try { System.out.print("*"); }
      catch (RuntimeException ex) { System.out.println(ex.getMessage()); }
     }
    System.out.println();
  } //

  private void Benchmark3() {
     for (int i = 0; i < 1000; i++) {
        try {
          System.out.print("*");
          throw new RuntimeException("OMG");
        }
        catch (RuntimeException ex) { }
     }
     System.out.println();
  } //

  private void Benchmark4b() {
    System.out.print("*");
    throw new RuntimeException("OMG");
  } //
  private void Benchmark4a() {
     for (int i = 0; i < 1000; i++) {
        try { Benchmark4b(); }
        catch (RuntimeException ex) { }
     }
     System.out.println();
  } //

  public static class StopWatch {
    private long _Start = System.nanoTime();       
    private long _Stop = System.nanoTime();

    public final void start() {
      _Start = System.nanoTime();
    }
    public final void stop() {
        _Stop = System.nanoTime();
        long lElapsedTicks = _Stop - _Start;          
        double lMilliseconds = lElapsedTicks / 1000000.0;
        System.out.println(lMilliseconds + " ms ");
        _Start = System.nanoTime();
     }
  } // class

  public final void test() {
    Calendar lNow1 = Calendar.getInstance();  // current time
    Calendar lNow2 = Calendar.getInstance();
    lNow2.add(Calendar.SECOND, 2);  // current time plus 2 seconds
    double lSince1970 = System.currentTimeMillis() / 1000.0; // Seconds since 1 Jan 1970      
    SimpleDateFormat lFormat = new SimpleDateFormat("dd MMM yyyy, HH:mm:ss");
    System.out.print("Current local time and date: ");
    System.out.println(lFormat.format(Calendar.getInstance().getTime()));

    // Benchmark
    Calendar lStart = Calendar.getInstance();
    Benchmark1();
    Calendar lEnd = Calendar.getInstance();       
    long lDuration = lEnd.getTimeInMillis() - lStart.getTimeInMillis();
    System.out.println("Time passed ms: " + lDuration);

    // Benchmarks: let's get more precise now
    StopWatch lStopwatch = new StopWatch();
    lStopwatch.start();
    System.out.print("Benchmark1:"); Benchmark1(); lStopwatch.stop();
    System.out.print("Benchmark2:"); Benchmark2(); lStopwatch.stop();
    System.out.print("Benchmark3:"); Benchmark3(); lStopwatch.stop();
    System.out.print("Benchmark4:"); Benchmark4a();lStopwatch.stop();

    try {
      System.in.read();
    } catch (IOException ex) {
      Logger.getLogger(Benchmark.class.getName()).log(Level.SEVERE, null, ex);
    }
  } // 

  public static void main(String[] args) {
    Benchmark lBenchmark = new Benchmark();
    lBenchmark.test();
  } //
  
} // class

example output:
Current local time and date: 09 May 2014, 18:03:20
****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
Time passed ms: 16
Benchmark1:****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
18.10766 ms
Benchmark2:****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
14.703688 ms
Benchmark3:****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
25.99924 ms
Benchmark4:****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
49.451426 ms

migration C#, Java, C++ (day 9), Recursion

logo
The Tower of Hanoi is one of the most classical examples. I would say that the it is a kind of medieval version of the Rubik’s Cube. I remember that we had to solve the Tower of Hanoi problem in Pascal at school around 1992. Yeah, the good old times. Basically we are talking about code that could be written with less than 15 lines. Anyway, this version here is far more verbose. It tells you the position of each disk for each move.

I was thinking about doing some more coding today. Maybe JSON or calling C# code from inside C++. The problem is that you always have to install something else in C++ to get things running. I only have a few hours per day to write the source code for this blog. I could post more complex ideas every once in a while, but this would contradict my precondition of 15 days. Today is day 9. I can tell you already that it is not enough to just learn these examples here. You have to look a bit on the left and right side as well to be as fit as a fiddle in C++ after just 15 days.

Tower of Hanoi

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DemoApp {
   class TowerOfHanoi {

      private class Stack {
         private Stack<int> _Stack = new Stack<int>();
         public readonly int StackId;

         public Stack(int xStackId) { StackId = xStackId; }
         public void push(int xDisk) { _Stack.Push(xDisk); }
         public int pop() { return _Stack.Pop(); }

         public void print() {
            Console.Write("stack" + StackId + ":  ");
            foreach (int lDisk in _Stack) Console.Write(lDisk + " ");
            Console.WriteLine();
         } //
      }; // class

      private Stack _Stack0 = new Stack(0);
      private Stack _Stack1 = new Stack(1);
      private Stack _Stack2 = new Stack(2);

      void SolveTowerOfHanoi(int xDisk, Stack xSource, Stack xTemp, Stack xDestination) {
         int lDisk;

         if (xDisk == 1) {
            lDisk = xSource.pop();
            xDestination.push(lDisk);
            Console.WriteLine("moving disk " + lDisk + " from " + xSource.StackId + " to " + xDestination.StackId);
            _Stack0.print();
            _Stack1.print();
            _Stack2.print();
            Console.WriteLine();
            return;
         } 

         SolveTowerOfHanoi(xDisk - 1, xSource, xDestination, xTemp);
         lDisk = xSource.pop();
         xDestination.push(lDisk);
         Console.WriteLine("moving disk " + lDisk + " from " + xSource.StackId + " to " + xDestination.StackId);
         _Stack0.print();
         _Stack1.print();
         _Stack2.print();
         Console.WriteLine();
         SolveTowerOfHanoi(xDisk - 1, xTemp, xSource, xDestination);
      } //

      public void test() {
         int lDiskCount;
         string s;
         
         do {
            Console.Write("number of disks: ");
            s = Console.ReadLine();
         }
         while (!int.TryParse(s, out lDiskCount));         
         
         Console.WriteLine();
         for (int i = lDiskCount; i > 0; --i) _Stack0.push(i);
         SolveTowerOfHanoi(lDiskCount, _Stack0, _Stack1, _Stack2);

         Console.ReadKey();
      } //

   } // class
} // namespace
package Demos;

import java.io.IOException;
import java.util.*;

public class TowerOfHanoi {

  private static class Stack {

    private final java.util.Stack<Integer> _Stack = new java.util.Stack<>();
    public int StackId;

    public Stack(int xStackId) {
      StackId = xStackId;
    }

    public final void push(int xDisk) {
      _Stack.push(xDisk);
    }

    public final int pop() {
      return _Stack.pop();
    }

    public final void print() {
      System.out.print("stack" + StackId + ":  ");
      for (int lDisk : _Stack) System.out.print(lDisk + " ");
      System.out.println();
    }
  } // class

  private final Stack _Stack0 = new Stack(0);
  private final Stack _Stack1 = new Stack(1);
  private final Stack _Stack2 = new Stack(2);

  private void SolveTowerOfHanoi(int xDisk, Stack xSource, Stack xTemp, Stack xDestination) {
    int lDisk;

    if (xDisk == 1) {
      lDisk = xSource.pop();
      xDestination.push(lDisk);
      System.out.println("moving disk " + lDisk + " from " + xSource.StackId + " to " + xDestination.StackId);
      _Stack0.print();
      _Stack1.print();
      _Stack2.print();
      System.out.println();
      return;
    }

    SolveTowerOfHanoi(xDisk - 1, xSource, xDestination, xTemp);
    lDisk = xSource.pop();
    xDestination.push(lDisk);
    System.out.println("moving disk " + lDisk + " from " + xSource.StackId + " to " + xDestination.StackId);
    _Stack0.print();
    _Stack1.print();
    _Stack2.print();
    System.out.println();
    SolveTowerOfHanoi(xDisk - 1, xTemp, xSource, xDestination);
  } //

  boolean tryParseInt(String xValue) {
    try {
      Integer.parseInt(xValue);
      return true;
    } catch (NumberFormatException ex) {
      System.out.println("NumberFormatException: " + ex.getMessage());
      return false;
    }
  } //

  public final void test() {
    String s = null;
    do {
      System.out.print("number of disks: ");
      s = new Scanner(System.in).nextLine();
    } while (!tryParseInt(s));
    int lDiskCount = Integer.parseInt(s);

    System.out.println();
    for (int i = lDiskCount; i > 0; --i) _Stack0.push(i);
    SolveTowerOfHanoi(lDiskCount, _Stack0, _Stack1, _Stack2);

    try {
      System.in.read();
    } catch (IOException ex) {
      System.out.println("IOException: " + ex.getMessage());
    }
  } //
} // class
#include<iostream>
#include<vector>

using namespace std;

class Stack{
private:
    vector<int> _Stack;
    int _StackId;

public:
    Stack (int xStackId) : _StackId(xStackId) { }
    int getStackId() { return _StackId; }

    void push(int xDisk){_Stack.push_back(xDisk);}
    int pop() {
        int lDisk = _Stack.back();
        _Stack.pop_back();
        return lDisk;
    } //

    void print() {
        cout << "stack" << _StackId << ":  ";
        for (int lDisk : _Stack) cout << lDisk << " ";
        cout << endl;
    } //
}; // class

Stack _Stack0(0);
Stack _Stack1(1);
Stack _Stack2(2);

void SolveTowerOfHanoi(int xDisk, Stack *xSource, Stack *xTemp, Stack *xDestination) {
    int lDisk;

    if (xDisk==1) {
      lDisk = xSource->pop();
      xDestination->push(lDisk);
      cout << "moving disk " << lDisk << " from " << xSource->getStackId() << " to " << xDestination->getStackId() << endl;
      _Stack0.print();
      _Stack1.print();
      _Stack2.print();
      cout << endl;
      return;
    }

    SolveTowerOfHanoi(xDisk-1, xSource, xDestination, xTemp);
    lDisk = xSource->pop();
    xDestination->push(lDisk);
    cout << "moving disk " << lDisk << " from " << xSource->getStackId() << " to " << xDestination->getStackId() << endl;
    _Stack0.print();
    _Stack1.print();
    _Stack2.print();
    cout << endl;
    SolveTowerOfHanoi(xDisk-1, xTemp, xSource, xDestination);
} //

int main() {
    int lDiskCount;
    cout << "number of disks: ";
    cin >> lDiskCount;
    cout << endl;
    if (cin.fail()) return -1; // error code

    for(int i=lDiskCount; i>0; --i) _Stack0.push(i);

    SolveTowerOfHanoi(lDiskCount, &_Stack0, &_Stack1, &_Stack2);

    cin.get();
    return 0;
} //

example output:
number of disks: 5

moving disk 1 from 0 to 2
stack0: 5 4 3 2
stack1:
stack2: 1

moving disk 2 from 0 to 1
stack0: 5 4 3
stack1: 2
stack2: 1

moving disk 1 from 2 to 1
stack0: 5 4 3
stack1: 2 1
stack2:

moving disk 3 from 0 to 2
stack0: 5 4
stack1: 2 1
stack2: 3

moving disk 1 from 1 to 0
stack0: 5 4 1
stack1: 2
stack2: 3

moving disk 2 from 1 to 2
stack0: 5 4 1
stack1:
stack2: 3 2

moving disk 1 from 0 to 2
stack0: 5 4
stack1:
stack2: 3 2 1

moving disk 4 from 0 to 1
stack0: 5
stack1: 4
stack2: 3 2 1

moving disk 1 from 2 to 1
stack0: 5
stack1: 4 1
stack2: 3 2

moving disk 2 from 2 to 0
stack0: 5 2
stack1: 4 1
stack2: 3

moving disk 1 from 1 to 0
stack0: 5 2 1
stack1: 4
stack2: 3

moving disk 3 from 2 to 1
stack0: 5 2 1
stack1: 4 3
stack2:

moving disk 1 from 0 to 2
stack0: 5 2
stack1: 4 3
stack2: 1

moving disk 2 from 0 to 1
stack0: 5
stack1: 4 3 2
stack2: 1

moving disk 1 from 2 to 1
stack0: 5
stack1: 4 3 2 1
stack2:

moving disk 5 from 0 to 2
stack0:
stack1: 4 3 2 1
stack2: 5

moving disk 1 from 1 to 0
stack0: 1
stack1: 4 3 2
stack2: 5

moving disk 2 from 1 to 2
stack0: 1
stack1: 4 3
stack2: 5 2

moving disk 1 from 0 to 2
stack0:
stack1: 4 3
stack2: 5 2 1

moving disk 3 from 1 to 0
stack0: 3
stack1: 4
stack2: 5 2 1

moving disk 1 from 2 to 1
stack0: 3
stack1: 4 1
stack2: 5 2

moving disk 2 from 2 to 0
stack0: 3 2
stack1: 4 1
stack2: 5

moving disk 1 from 1 to 0
stack0: 3 2 1
stack1: 4
stack2: 5

moving disk 4 from 1 to 2
stack0: 3 2 1
stack1:
stack2: 5 4

moving disk 1 from 0 to 2
stack0: 3 2
stack1:
stack2: 5 4 1

moving disk 2 from 0 to 1
stack0: 3
stack1: 2
stack2: 5 4 1

moving disk 1 from 2 to 1
stack0: 3
stack1: 2 1
stack2: 5 4

moving disk 3 from 0 to 2
stack0:
stack1: 2 1
stack2: 5 4 3

moving disk 1 from 1 to 0
stack0: 1
stack1: 2
stack2: 5 4 3

moving disk 2 from 1 to 2
stack0: 1
stack1:
stack2: 5 4 3 2

moving disk 1 from 0 to 2
stack0:
stack1:
stack2: 5 4 3 2 1

Lazy class (advanced)

OK, this is no joke. This class really exists.

Not many programmers come across this class. At first glance it seems to be useless. Let’s have a closer look!
Imagine the following situation. You are initializing objects. Each of them takes a lot of time and uses many resources. There is no load balancing. Some of them may be used simultaneously, so it is not one or the other. The problem is that you don’t know, which object will be needed. It could be that you don’t need any object at all. Nevertheless you want to provide each object to the system as if they were initialized already. That way you don’t have to deal with any initialization later during the running program.

I included a short example about Bill Gates. Let’s say he has too many cars, so he feels the agony of choice. Early in the morning Bill is standing in his garage. He needs a car to go to the beach, but there are simply too many cars to choose from. One of his many gardeners recommends the Porsche. Well, the final choice is at the whim of Bill Gates. Hypothetically he might also need a second car today. His new 19-year-old girl friend had an overnight stay … on the couch. Yeah, that’s life šŸ˜‰

I am sure you are often using LINQ. It uses a similar concept. Linq does not execute until the point you access the query result. When the example program calls “… select x.GetSomething()”, then you do not see any output from the method GetSomething(), which would be “What about an ice cream?”. “Where is my lunch?” prints first. And when we call lYummy.First() to get the first object from the query (well, there is only one), only then you see GetSomething() executing.

Too Lazy or just right?

using System;
using System.Collections.Generic;
using System.Linq;

namespace DemoApp {
   public class VeryLazy {
      public class NotNow {
         public NotNow() { Console.WriteLine("executing constructor"); }
         public void DoSomething() { Console.WriteLine("Zzzzzz... Jeez, you woke me up!"); }
         public string GetSomething() {
            Console.WriteLine("What about an ice cream?");
            return "ice cream";
         }
      } // class      

      public class BillGatesGarage {
         Lazy<NotNow> Porsche = new Lazy<NotNow>();
         Lazy<NotNow> Ferrari = new Lazy<NotNow>();
         Lazy<NotNow> Maserati = new Lazy<NotNow>();
         Lazy<NotNow> Lamborghini = new Lazy<NotNow>();

         public enum eCoolness { eSmart, eShowOff, eFast, eFun }
         public NotNow getCar(eCoolness xCoolness) {
            Console.WriteLine();
            Console.WriteLine("Sleeping cars:");
            Console.WriteLine("Porsche " + !Porsche.IsValueCreated);
            Console.WriteLine("Ferrari " + !Ferrari.IsValueCreated);
            Console.WriteLine("Maserati " + !Maserati.IsValueCreated);
            Console.WriteLine("Lamborghini " + !Lamborghini.IsValueCreated);
            switch (xCoolness) {
               case eCoolness.eSmart: return Porsche.Value;
               case eCoolness.eShowOff: return Ferrari.Value;
               case eCoolness.eFast: return Maserati.Value;
               case eCoolness.eFun: return Lamborghini.Value;
            }
            return null;
         }
      } // class

      public static void test() {
         List<NotNow> lList = new List<NotNow>();
         lList.Add(new NotNow());         
         var lYummy = from x in lList select x.GetSomething();
         Console.WriteLine("Where is my lunch?");
         Console.WriteLine("I got an " + lYummy.First() + " for you!");

         Console.WriteLine();
         Lazy<NotNow> lLazy = new Lazy<NotNow>();
         Console.WriteLine("Object created already? " + lLazy.IsValueCreated);
         Console.WriteLine("No worries! Let's create an instance now:");
         NotNow lNotNow = lLazy.Value;
         Console.WriteLine("Object created already? " + lLazy.IsValueCreated);
         if (lLazy.IsValueCreated) lNotNow.DoSomething();

         Console.WriteLine();
         Console.WriteLine("All engines warmed up?");
         BillGatesGarage lGarage = new BillGatesGarage();
         NotNow lBrumBrum = lGarage.getCar(BillGatesGarage.eCoolness.eSmart);
         lBrumBrum.DoSomething();
         Console.WriteLine("Oh dear, he just changed his mind");
         NotNow lWhooosh = lGarage.getCar(BillGatesGarage.eCoolness.eFast);
         lWhooosh.DoSomething();

         // check cars
         lGarage.getCar(BillGatesGarage.eCoolness.eFast);
      } //

   } // class
} // namespace

example output:
executing constructor
Where is my lunch?
What about an ice cream?
I got an ice cream for you!

Object created already? False
No worries! Let’s create an instance now:
executing constructor
Object created already? True
Zzzzzz… Jeez, you woke me up!

All engines warmed up?

Sleeping cars:
Porsche True
Ferrari True
Maserati True
Lamborghini True
executing constructor
Zzzzzz… Jeez, you woke me up!
Oh dear, he just changed his mind

Sleeping cars:
Porsche False
Ferrari True
Maserati True
Lamborghini True
executing constructor
Zzzzzz… Jeez, you woke me up!

Sleeping cars:
Porsche False
Ferrari True
Maserati False
Lamborghini True

migration C#, Java, C++ (day 9b), TextIO, Tuples

logo

Some basic stuff today. Tuples are not well-known. They are very useful for returning multiple types within one method. Tuples are immutable in C#. C++ is more relaxed about this. Reading or writing text files is nothing special as well. Anyway, consider that C# uses Unicode whereas C++ mainly deals with ASCII. I do not see C++ as a standard developer language. It is used to gain extra speed. So I won’t even touch the idea of Unicode in my examples. What is the meaning of sitting in front of your PC for hours just to get a result that you could get in another language within minutes.
The right approach is to mix languages.

TextIO and Tuples

using System;
using System.IO;

namespace DemoApp.ToCpp {
   public class Day9 {

      public static void Test() {
         try {
            string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
            string lFile = lDesktopPath + "test.txt";
            string lText = "I see trees of green, red roses too.\nI see them bloom for me and you.\nAnd I think to myself what a wonderful world.\nI see skies of blue and clouds of white\n...";

            // write
            File.WriteAllText(lFile, lText);
            
            // read
            string lLoadResult = File.ReadAllText(lFile);
            Console.WriteLine(lLoadResult);

            // make trees blue
            lLoadResult = lLoadResult.Replace("green", "blue");
            Console.WriteLine("\nThe trees have just changed their colour:\n\n");
            Console.WriteLine(lLoadResult);

            // tuples
            Tuple<int, string, double> lTuple = new Tuple<int, string, double>(1, "2", 3.0);
            //lTuple.Item3 = 3.3; compiler error: readonly
            Console.WriteLine(lTuple.Item1 + " " + lTuple.Item2 + " " + lTuple.Item3);
         }
         catch (Exception ex) {
            Console.WriteLine(ex.Message);
         }

         Console.ReadLine();
      } //

   } // class
} // namespace

example output:
I see trees of green, red roses too.
I see them bloom for me and you.
And I think to myself what a wonderful world.
I see skies of blue and clouds of white

The trees have just changed their colour:

I see trees of blue, red roses too.
I see them bloom for me and you.
And I think to myself what a wonderful world.
I see skies of blue and clouds of white

1 2 3

#include <iostream>
#include <string>
#include <stdexcept>
#include <memory>
#include <sstream>
#include <fstream>
#include <algorithm>

using namespace std;

void StringToTextFile(const string &xFilename, const string &xText)  {
    ofstream lStream;
    lStream.open(xFilename.c_str());
    lStream << xText;
    lStream.close();
}

string TextFileToString(const string &xFilename)  {
    ostringstream lStringBuilder(ios::out | ios::binary);
    ifstream lStream(xFilename.c_str());
    string s;
    while (getline(lStream, s)) lStringBuilder << s << endl;
    return lStringBuilder.str();
}

int main() {
    try {
      string lDesktopPath = "C:\\Users\\YourUsername\\Desktop\\"; // no direct and portable C++ support
      string lFile = lDesktopPath + "test.txt";
      string lText = "I see trees of green, red roses too.\nI see them bloom for me and you.\nAnd I think to myself what a wonderful world.\nI see skies of blue and clouds of white\n...";

      // write
      StringToTextFile(lFile, lText);

      // read
      string lLoadResult = TextFileToString(lFile);
      cout << lLoadResult << endl;

      // make trees blue
      lLoadResult.replace(lLoadResult.find("green"), 5, "blue");
      cout << endl << "The trees have just changed their colour:" << endl << endl;
      cout << lLoadResult << endl;

      // tuples
      tuple<int, string, double> lTuple = make_tuple(1, "2", 3.0);
      cout << get<0>(lTuple) << " " << get<1>(lTuple) << " " << get<2>(lTuple) << endl;
      get<0, int, string, double>(lTuple) = 9;
      cout << get<0>(lTuple) << " " << get<1>(lTuple) << " " << get<2>(lTuple) << endl;

      int i;
      string s = "OOOPS";
      double d;
      tie (i, ignore, d) = lTuple; // unpack
      cout << i << " " << s << " " << d << endl;

      s = get<1>(lTuple);
      cout << s << endl;
    }
    catch (exception &ex) { cout << ex.what() << endl; }

    cin.get();
    return 0;
} //

example output:
I see trees of green, red roses too.
I see them bloom for me and you.
And I think to myself what a wonderful world.
I see skies of blue and clouds of white

The trees have just changed their colour:

I see trees of blue, red roses too.
I see them bloom for me and you.
And I think to myself what a wonderful world.
I see skies of blue and clouds of white

1 2 3
9 2 3
9 OOOPS 3
2

package DemoApp.ToCpp;

public final class Tuple<T, U, V> {

  // tuples are not supported by the standard libraries
  // this is a quick and dirty way to replicate a Triplet
  private final T _value0;
  private final U _value1;
  private final V _value2;

  public Tuple(T xValue0, U xValue1, V xValue2) {
    _value0 = xValue0;
    _value1 = xValue1;
    _value2 = xValue2;
  } // constructor

  public T get0() { return _value0; }
  public U get1() { return _value1; }
  public V get2() { return _value2; }
} // class
//-----------------------------------------------------------------------------
package DemoApp.ToCpp;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

public class Day9 {  

  public static void main(String[] args) {
    try {
      String lDesktopPath = null;

      try {
        lDesktopPath = System.getProperty("user.home") + "/Desktop";
        lDesktopPath = lDesktopPath.replace("\\", "/");
        System.out.println("Desktop path: " + lDesktopPath);
      } catch (Exception e) {
        System.out.println("Exception caught =" + e.getMessage());
      }

      String lFile = lDesktopPath + "/test.txt";
      String lNewLine = System.getProperty("line.separator");
      String lText = "I see trees of green, red roses too." + lNewLine + "I see them bloom for me and you." + lNewLine + "And I think to myself what a wonderful world." + lNewLine + "I see skies of blue and clouds of white" + lNewLine + "...";

      // write  (try-with-resources statement, http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html)
      try (BufferedWriter lWriter = new BufferedWriter(new FileWriter(lFile))) {
        lWriter.write(lText);
      } catch (IOException ex) {
        System.out.println("IOException =" + ex.getMessage());
      }

      // read (all in one shot)
      StringBuilder lStringBuilder = new StringBuilder();
      String lLoadResult = null;
      try {
        Path lPath = Paths.get(lFile);
        List<String> lList = Files.readAllLines(lPath, StandardCharsets.UTF_8);
        for (String lLine : lList) { lStringBuilder.append(lLine); lStringBuilder.append(lNewLine); }
        lLoadResult = lStringBuilder.toString();        
        System.out.println(lLoadResult);
      } catch (IOException ex) {
        System.out.println("IOException =" + ex.getMessage());
      }

      // make trees blue
      if (lLoadResult != null) {
        lLoadResult = lLoadResult.replace("green", "blue");
        System.out.println("The trees have just changed their colour:\n");
        System.out.println(lLoadResult);
      }

      // tuples      
      Tuple<Integer, String, Double> lTuple = new Tuple<>(1, "2", 3.0);
      //lTuple.Item3 = 3.3; compiler error: readonly
      System.out.println(lTuple.get0() + " " + lTuple.get1() + " " + lTuple.get2());
    } catch (RuntimeException ex) {
      System.out.println(ex.getMessage());
    }

    new Scanner(System.in).nextLine();
  }

} // class

example output:
Desktop path: C:/Users/LvAdmin/Desktop
I see trees of green, red roses too.
I see them bloom for me and you.
And I think to myself what a wonderful world.
I see skies of blue and clouds of white

The trees have just changed their colour:

I see trees of blue, red roses too.
I see them bloom for me and you.
And I think to myself what a wonderful world.
I see skies of blue and clouds of white

1 2 3.0