Blog Archives

JSON to LINQ to JSON

JsonLinqJson

It is about 9:30pm and I am sitting on the train home. It has been quite tough organizing my life in the last months. This is not the promised follow-up on OpenGL. There has been no time to read any book.

Anyway, this post shows how to run LINQ queries on JSON. The use can be quite broad. Imagine a shell command executing your LINQ on any JSON file. You can query into depth and build complex return tree structures with 2 or more levels. LINQ itself is highly flexible. And – just to give it the right flair – it returns results in the good old JSON format.

You could even go a bit further and insert more than just a LINQ query. In theory, you could add any C# code at run-time. The changes to this program would be minuscule.

I have sufficiently commented the code. There is a minimum overhead to get the job done.

 

How it works:

  1. The JSON data is imported using the JavaScriptSerializer, which is part of the System.Web.Extensions library reference. We try to force the result into a Dictionary<string, object>. Do not lazily use ‘object’. Some type information would get lost. I played with this and got eg. arrays instead of ArrayLists.
  2. The resulting structure is then used to build classes. These are converted to legible C# source code.
  3. The LINQ command is inserted. You can see the result in the TextBox titled ‘C# source code output’.
  4. A compiler instance is created and some library references are added. We compile the generated source code at run-time and then execute the binary.
  5. A standard JSON object is returned, rudimentary formatted and displayed as the final result.

Btw. I have inserted a horizontal and a vertical GridSplitter into the WPF XAML. You can easily change the size of the TextBoxes while playing with this tool. A few JSON examples were also added.

<Window         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="LinqJson.MainWindow"   Title="JSON LINQ JSON" Height="Auto" Width="876" d:DesignHeight="695">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="1*"/>
      <RowDefinition Height="43*"/>
      <RowDefinition Height="30"/>
      <RowDefinition Height="150*"/>
      <RowDefinition Height="30"/>
      <RowDefinition Height="150*"/>
      <RowDefinition Height="10*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="10*"/>
      <ColumnDefinition Width="250*"/>
      <ColumnDefinition Width="10"/>
      <ColumnDefinition Width="250*"/>
      <ColumnDefinition Width="10*"/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Button Content="convert" Width="50" Height="30" Click="Button_Click" Grid.Row="1" HorizontalAlignment="Left" Grid.Column="1" Margin="0,8,0,7"/>

    <StackPanel Grid.Column="1" Grid.Row="2" VerticalAlignment="Bottom" HorizontalAlignment="Left" Orientation="Horizontal" Height="27" >
      <Label Content="JSON input" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="0,1" />
      <ComboBox Width="240" Margin="50,3,3,3" SelectionChanged="ComboBox_SelectionChanged" VerticalAlignment="Bottom">
        <ComboBoxItem  Name="ex1">Example 1</ComboBoxItem>
        <ComboBoxItem  Name="ex2">Example 2</ComboBoxItem>
        <ComboBoxItem  Name="ex3">Example 3</ComboBoxItem>
        <ComboBoxItem  Name="ex4">Example 4</ComboBoxItem>
      </ComboBox>
    </StackPanel>
    <TextBox  x:Name="JsonIn" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Grid.Column="1" Grid.Row="3"/>

    <GridSplitter Grid.Column="2" Grid.RowSpan="999" HorizontalAlignment="Stretch" Width="10" Background="Transparent" ResizeBehavior="PreviousAndNext"/>
    <GridSplitter Grid.Row="4" Grid.ColumnSpan="999" VerticalAlignment="Top" HorizontalAlignment="Stretch"  Height="10" Background="Transparent" ResizeBehavior="PreviousAndNext" />

    <Label Content="JSON output" Grid.Column="3" Grid.Row="2"  VerticalAlignment="Bottom" />
    <TextBox x:Name="JsonOut" VerticalAlignment="Stretch" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Grid.Column="3"  Grid.Row="3"  />

    <Label Content="C# source code output" Grid.Column="3" Grid.Row="4"  VerticalAlignment="Bottom" />
    <TextBox x:Name="CSharpSourceCode" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Grid.Column="3" Grid.Row="5" />
    <Label Content="LINQ input" Grid.Column="1" Grid.Row="4"  VerticalAlignment="Bottom"/>
    <TextBox x:Name="LinqIn" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Grid.Column="1" Grid.Row="5" />

  </Grid>

</Window>
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace LinqJson {
  public partial class MainWindow : Window {

    public MainWindow() {
      InitializeComponent();
    } // constructor

    private void Button_Click(object sender, RoutedEventArgs e) {

      // --------------------------------------------------
      // get the JSON input
      // --------------------------------------------------

      string lJsonIn = JsonIn.Text;

      // --------------------------------------------------
      // construct the C# source code (class hierarchy)
      // --------------------------------------------------

      Converter lConverter = new Converter();
      Dictionary<string, object> lTree = lConverter.JsonToDictionary(lJsonIn);
      lConverter.DictionariesToClasses("root", 0, lTree);

      var lStringBuilder = new StringBuilder();
      lConverter.BuildClasses(lStringBuilder);
      string lCSharpSourceCode = lConverter.GetUsings() + lStringBuilder.ToString();

      // --------------------------------------------------
      // add the LINQ command to the source code
      // --------------------------------------------------

      string lLinq = LinqIn.Text;
      string lEntryPoint = "\n\n";
      lEntryPoint += "public class baseClass {\n";
      lEntryPoint += "  public static object executeLinq(string xJson) {\n";
      lEntryPoint += "    xJson = xJson.Trim();";
      lEntryPoint += "    if (xJson[0] == '[') xJson = \"{home: \" + xJson + \"}\";";
      lEntryPoint += "    var lSerializer = new JavaScriptSerializer();\n";
      lEntryPoint += "    var root = lSerializer.Deserialize<Class_root>(xJson);\n";
      lEntryPoint += "    var lResult = " + lLinq.Replace("\n", "\n        ") + ";\n";
      lEntryPoint += "    return lSerializer.Serialize(lResult);\n";
      lEntryPoint += "  }\n";
      lEntryPoint += "}\n";
      lCSharpSourceCode += lEntryPoint;

      // --------------------------------------------------
      // display the source code
      // --------------------------------------------------

      CSharpSourceCode.Text = lCSharpSourceCode;

      // --------------------------------------------------
      // compile the source code
      // --------------------------------------------------

      var lProviderOptions = new Dictionary<string, string>();
      lProviderOptions.Add("CompilerVersion", "v4.0");
      var lCSharpCodeProvider = new CSharpCodeProvider(lProviderOptions);
      var lCompilerParameters = new CompilerParameters();

      lCompilerParameters.ReferencedAssemblies.Add("System.dll");
      lCompilerParameters.ReferencedAssemblies.Add("System.Core.dll");
      lCompilerParameters.ReferencedAssemblies.Add("System.Data.Linq.dll");
      lCompilerParameters.ReferencedAssemblies.Add("System.Threading.dll");
      lCompilerParameters.ReferencedAssemblies.Add("System.Web.Extensions.dll");
      lCompilerParameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");
      lCompilerParameters.GenerateInMemory = true;
      lCompilerParameters.GenerateExecutable = false;  // not required, we don't have a Main() method
      lCompilerParameters.IncludeDebugInformation = true;
      var lCompilerResults = lCSharpCodeProvider.CompileAssemblyFromSource(lCompilerParameters, lCSharpSourceCode);

      if (lCompilerResults.Errors.HasErrors) {
        var lError = new StringBuilder();

        foreach (CompilerError lCompilerError in lCompilerResults.Errors) {
          lError.AppendLine(lCompilerError.ErrorNumber + " => " + lCompilerError.ErrorText + Environment.NewLine);
        }

        JsonOut.TextWrapping = TextWrapping.Wrap;
        JsonOut.Text = lError.ToString();
        return;
      }
      JsonOut.TextWrapping = TextWrapping.NoWrap;

      // --------------------------------------------------
      // execute the compiled code
      // --------------------------------------------------

      Assembly lAssembly = lCompilerResults.CompiledAssembly;
      Type lProgram = lAssembly.GetType("baseClass");
      MethodInfo lMethod = lProgram.GetMethod("executeLinq");

      object lQueryResult = lMethod.Invoke(null, new object[] { lJsonIn });   // returns a JSON string object

      // --------------------------------------------------
      // rudimentary JSON output formatting
      // --------------------------------------------------

      string lJsonOut = lQueryResult.ToString();
      lJsonOut = lJsonOut.Replace(",", ",\n");
      lJsonOut = lJsonOut.Replace(",{", ",{\n");
      lJsonOut = lJsonOut.Replace("]", "]\n");
      JsonOut.Text = lJsonOut;
    } //

    private void ComboBox_SelectionChanged(object xSender, SelectionChangedEventArgs e) {
      var lComboBox = xSender as ComboBox;

      switch (lComboBox.SelectedIndex) {
        case 0:
          JsonIn.Text = "{\n \"number\": 108.541,\n \"datetime\": \"1975-03-13T10:30:00\" ,\n \"serialnumber\": \"SN1234\",\n \"more\": {\n  \"field1\": 123,\n  \"field2\": \"hello\"\n },\n \"array\": [\n  {\"x\": 2.0},\n  {\"x\": 3.0},\n  {\"x\": 4.0}\n ]\n}";
          LinqIn.Text = "from a in root.array\nwhere a.x > 2.0M\nselect a";
          break;
        case 1:
          JsonIn.Text = "[ 1, 9, 5, 7, 1, 4 ]";
          LinqIn.Text = "from a in root.home\nwhere ((a == 4) || (a == 1))\nselect a";
          break;
        case 2:
          JsonIn.Text = "{myLuckyNumbers: [ 1, 9, 5, 26, 7, 1, 4 ]}";
          LinqIn.Text = "from x in root.myLuckyNumbers\nwhere x % 2 == 0\nselect new { simple=x, square=x*x, text=\"Field\"+x.ToString(), classInClass=new {veryOdd=x/7.0, lol=\":D\"}}";
          break;
        case 3:
          string s;
          s = "{\"mainMenu\": {\n";
          s += "  \"info\": \"Great tool.\",\n";
          s += "  \"value\": 100.00,\n";
          s += "  \"menu\": {\n";
          s += "  \"subMenu\": [\n";
          s += "    {\"text\": \"New\", \"onclick\": \"NewObject()\"},\n";
          s += "    {\"text\": \"Open\", \"onclick\": \"Load()\"},\n";
          s += "    {\"text\": \"Close\", \"onclick\": \"ByeBye()\"},\n";
          s += "    {\"text\": \"NNN\", \"onclick\": \"Useless()\"}\n";
          s += "  ]}\n";
          s += "}}\n";
          JsonIn.Text = s;
          LinqIn.Text = "root.mainMenu.menu.subMenu.Where(t => t.text.StartsWith(\"N\")).Last();";

          break;
        default:
          break;
      }
    } //

  } // class
} // namespace
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;  // requires reference to System.Web.Extensions, used by the JavaScriptSerializer

namespace LinqJson {
  public class Converter {

    private Dictionary<string, object> _Classes = new Dictionary<string, object>();

    public string GetUsings() {
      string s;
      s = "using System;\n";
      s += "using System.Collections.Generic;\n";
      s += "using System.Linq;\nusing System.Text;\n";
      s += "using System.Threading.Tasks;\n";
      s += "using System.Collections;\n";
      s += "using System.Web.Script.Serialization;\n\n";
      return s;
    } //

    public Dictionary<string, object> JsonToDictionary(string xJson) {
      xJson = xJson.Trim();
      if (xJson[0] == '[') xJson = "{home: " + xJson + "}";  // nameless arrays cannot be converted to dictionaries

      var lJavaScriptSerializer = new JavaScriptSerializer();
      try { return lJavaScriptSerializer.Deserialize<Dictionary<string, object>>(xJson); }
      catch (Exception) { return null; }
    } //

    public void BuildClasses(StringBuilder xStringBuilder) {
      foreach (var lClass in _Classes) {
        Dictionary<string, object> lMembers = lClass.Value as Dictionary<string, object>;
        if (lMembers == null) continue;
        if (lMembers.Count <= 0) continue;

        xStringBuilder.Append("public class Class_");
        xStringBuilder.Append(lClass.Key);
        xStringBuilder.AppendLine(" {");

        foreach (var lMember in lMembers) {
          object lValue = lMember.Value;
          string lKey = lMember.Key;
          Type lType = (lValue == null) ? typeof(object) : lMember.Value.GetType();

          xStringBuilder.Append(new String(' ', 2));
          xStringBuilder.Append("public ");

          if (lType.IsValueType || (lValue is string)) {
            xStringBuilder.Append(lType.Name);
            xStringBuilder.Append(" ");
            xStringBuilder.Append(lKey);
            xStringBuilder.AppendLine(";");
          }
          else if (lValue is Dictionary<string, object>) {
            xStringBuilder.Append("Class_");
            xStringBuilder.Append(lKey);
            xStringBuilder.Append(" ");
            xStringBuilder.Append(lKey);
            xStringBuilder.AppendLine(";");
          }
          else if (lValue is ArrayList) {
            ArrayList lArrayList = lValue as ArrayList;
            var lMemberType = ArrayListType(lArrayList);   // differentiate between the contents of the list
            if (lMemberType.IsValueType || (lMemberType.Name == "String")) {
              //xStringBuilder.Append(lMemberType.Name.Replace("`2")); // Dictionaries use name "Dictionary`2"
              xStringBuilder.Append(" List<");               xStringBuilder.Append(lMemberType.Name);               xStringBuilder.Append("> ");
              xStringBuilder.Append(lKey);
              xStringBuilder.AppendLine(";");
            }
            else {
              // a class
              xStringBuilder.Append("List<Class_" + lKey + "> ");
              xStringBuilder.Append(lKey);
              xStringBuilder.AppendLine(";");
            }
          }
        }
        xStringBuilder.AppendLine(" }");
        xStringBuilder.AppendLine();
      }
    } //

    public void DictionariesToClasses(string xRootName, int xIndent, object xObject) {
      if (xObject == null) return;
      Type lType = xObject.GetType();

      if (lType.IsValueType || (xObject is string)) return;
      var lDictionary = xObject as Dictionary<string, object>;
      if (lDictionary != null) {
        object lObj;
        if (!_Classes.TryGetValue(xRootName, out lObj)) {
          _Classes.Add(xRootName, lDictionary);
        }
        else {
          foreach (var lKeyValuePair in lDictionary) {
            // This is a weakness of the program.
            // Two JSON objects must not use the same name.
            // We would have to compare the JSON objects and determine wether to create multiple or just one C# class.
            _Classes[lKeyValuePair.Key] = lKeyValuePair.Value;  // object type will be overridden !!!!!!
          }
          return;
        }

        foreach (var lKeyValuePair in lDictionary) {
          DictionariesToClasses(lKeyValuePair.Key, xIndent, lKeyValuePair.Value);
        }
        return;
      }

      var lArrayList = xObject as ArrayList;
      if (lArrayList != null) {
        object lObj;
        if (!_Classes.TryGetValue(xRootName, out lObj)) {
          lDictionary = new Dictionary<string, object>();
          _Classes.Add(xRootName, lDictionary);
        }
        else lDictionary = lObj as Dictionary<string, object>;

        var lElementType = ArrayListType(lArrayList);
        if (lElementType == typeof(Dictionary<string, object>)) {
          var lList = lArrayList.Cast<Dictionary<string, object>>().ToList(); // upgrade our object to have stronger types
          foreach (var lDict in lList) {
            foreach (var lKeyValuePair in lDict) {
              lDictionary[lKeyValuePair.Key] = lKeyValuePair.Value;  // object type will be overridden !!!!!!
            }
          }
          foreach (var lKeyValuePair in lDictionary) {
            DictionariesToClasses(lKeyValuePair.Key, xIndent, lKeyValuePair.Value);
          }
        }
        return;
      }
    } //

    private void Append(StringBuilder xStringbuilder, int xIndent, string xString) {
      xStringbuilder.Append(new String(' ', xIndent));
      xStringbuilder.Append(xString);
    } //

    private void Newline(StringBuilder xStringbuilder) {
      xStringbuilder.AppendLine();
    } //

    private Type ArrayListType(ArrayList xArrayList) {
      var lTypes = new Dictionary<string, Type>();
      Type lType;
      string lTypeName;

      foreach (object o in xArrayList) {
        if (o == null) lType = typeof(object);
        else lType = o.GetType();
        lTypeName = lType.Name;
        if (!lTypes.ContainsKey(lTypeName)) lTypes.Add(lTypeName, lType);
      }

      if (lTypes.Count == 1) return lTypes.Values.First();  // distinct
      return typeof(object);
    } //

  } // class
} // namespace
Advertisements

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

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

migration C#, Java, C++ (day 8), TCP, Qt

The last two days were characterized by incompatibilities. There is no TCP support in the C++ standard libraries. I knew why I tried to avoid Boost and Qt. Visual Studio 2013 has its problems with Boost. You get really weird error messages that cannot be fixed at all. Why should Visual Studio actively support Boost? They have their own libraries, which are pretty cool.
I was fighting with several compilers and tools that generated roughly 50 GB of library data in total. Finally I gave up as it is really as mad as it is described on some web pages. I ended up installing the Qt Creator and had to rewrite the entire C++ code. I have to say that Qt is structured in a much better way than Boost. Overall the C++ coding took roughly 20 times longer than the C# coding, which was completed within 20 minutes. Most of that time was due to the incompatibility fight.
C++ executes faster, but let’s take a company’s point of view. If someone invests 250,000 USD in extra production time, wouldn’t it be smarter to buy 10x faster computers instead?
In the stock market, there are many traders telling you that it is all about speed. Not really! Data is coming in a sequence. I do know many situations where you have to slow down the system to solve specific sequence problems. Most market participant aren’t even aware of this.

Today I compare two TCP chat programs. C# is straight forward. The C++ side looks a bit weird, because the Qt Q_OBJECT macro requires header files. I did not want to split up the code into many pieces, so I kept the code short and I also kept it in the header sections.

ADDENDUM 21 May 2014

The Java code has been added today. The code structure is better than the C# example. The short Java test program deals with 1 server and 2 clients.

Below the Java code you can find a second C# example that uses the improved object approach similar to the Java code.

TCP

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace DemoApp.ToCpp {
   public class Day8 {

      public class NetworkListener {

         private volatile bool _ExitLoop = true;
         private TcpListener _Listener;

         public int Port { get; private set; }
         public string IpAddress { get; private set; }
         public string ThreadName { get; private set; }
         private List<Client> _AllClients = new List<Client>();

         public NetworkListener(string xIpAddress, int xPort, string xThreadName) {
            Port = xPort;
            IpAddress = xIpAddress;
            ThreadName = xThreadName;
         } //

         private class Client {
            private TcpClient _TcpClient;
            private NetworkStream _NetworkStream;
            public delegate void dOnShutDown(Client xClient);
            public event dOnShutDown OnShutDown;

            private volatile bool _ExitLoop = false;
            public readonly string IpAddress;

            public Client(TcpClient xTcpClient) {
               _TcpClient = xTcpClient;
               IpAddress = xTcpClient.Client.LocalEndPoint.ToString();
               Console.WriteLine("SERVER MESSAGE -> " + IpAddress + " new client connected.");
            } //

            public void Disconnect() {
               _ExitLoop = true;
               dOnShutDown lEvent = OnShutDown;
               if (lEvent != null) lEvent(this);
               if (_NetworkStream != null) _NetworkStream.WriteTimeout = 5; // immediate timeout
            } //

            public void ListenAsync() {
               Thread lThread = new Thread(new ThreadStart(Listen));
               lThread.IsBackground = true;
               lThread.Name = "Client_" + IpAddress;
               lThread.Start();
            } //

            private void Listen() {
               using (_NetworkStream = _TcpClient.GetStream()) {
                  using (StreamReader lStreamReader = new StreamReader(_NetworkStream)) {
                     while (!_ExitLoop) {
                        try {
                           string s = lStreamReader.ReadLine();
                           if (s == null) break;
                           Console.WriteLine("SERVER MESSAGE -> " + IpAddress + " Client said: " + s);
                        }
                        catch (IOException) {
                           if (_ExitLoop) Console.WriteLine("SERVER MESSAGE -> user requested TcpClient shutdown");
                           else Console.WriteLine("SERVER MESSAGE -> disconnected");
                        }
                        catch (Exception ex) { Console.WriteLine(ex.Message); }
                     }
                  }
                  Console.WriteLine("SERVER MESSAGE -> " + IpAddress + " Listener is shutting down");
               }
               _NetworkStream = null;
            } //
         } // class

         public bool WaitForClientsAsync() {
            if (!_ExitLoop) {
               Console.WriteLine("SERVER MESSAGE -> Listener running already");
               return false;
            }
            _ExitLoop = false;

            try {
               _Listener = new TcpListener(IPAddress.Parse(IpAddress), Port);
               _Listener.Start();

               Thread lThread = new Thread(new ThreadStart(WaitForAClient));
               lThread.IsBackground = true;
               lThread.Name = ThreadName;
               lThread.Start();

               return true;
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
            return false;
         } //

         public void Disconnect() {
            _ExitLoop = true;
            List<Client> lClone; // we need a clone, because the _AllClients collection will be manipulated during the foreach loop
            lock (_AllClients) { lClone = new List<Client>(_AllClients); }
            foreach (Client lClient in lClone) lClient.Disconnect();
            _Listener.Stop();
         } //

         private void WaitForAClient() {
            try {
               while (!_ExitLoop) {
                  int lClientCount;
                  lock (_AllClients) lClientCount = _AllClients.Count;
                  Console.WriteLine("SERVER MESSAGE -> Waiting for client number " + lClientCount);
                  TcpClient lTcpClient = _Listener.AcceptTcpClient();
                  Client lClient = new Client(lTcpClient);
                  lClient.OnShutDown += OnClientShutDown;
                  lock (_AllClients) _AllClients.Add(lClient);
                  lClient.ListenAsync(); // starts a background thread
               }
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
         }

         void OnClientShutDown(NetworkListener.Client xClient) {
            lock (_AllClients) { _AllClients.Remove(xClient); }
         } //         

      } // class

      public class NetworkClient {
         public int Port { get; private set; }
         public string IpAddress { get; private set; }
         public string ThreadName { get; private set; }

         private NetworkStream _NetworkStream = null;
         private bool _ExitLoop = true;
         private BlockingCollection<string> _Queue = new BlockingCollection<string>();

         public NetworkClient(string xIpAddress, int xPort, string xThreadName) {
            Port = xPort;
            IpAddress = xIpAddress;
            ThreadName = xThreadName;
         } //

         public void ConnectAsync() {
            if (!_ExitLoop) return; // running already
            _ExitLoop = false;

            Thread lThread = new Thread(new ThreadStart(Loop));
            lThread.IsBackground = true;
            lThread.Name = ThreadName;
            lThread.Start();
         } //

         public void Disconnect() {
            _ExitLoop = true;
            _Queue.Add(null);
            if (_NetworkStream != null) _NetworkStream.ReadTimeout = 100;
         } //

         public void Send(string xText) {
            if (string.IsNullOrEmpty(xText)) return;
            _Queue.Add(xText); // thread safety by context switching
         } //

         private void Loop() {
            try {
               using (TcpClient lClient = new TcpClient()) {
                  lClient.Connect(IpAddress, Port);
                  using (_NetworkStream = lClient.GetStream()) {
                     using (StreamWriter lStreamWriter = new StreamWriter(_NetworkStream)) {
                        while (!_ExitLoop) {
                           try {
                              string s = _Queue.Take();
                              if (string.IsNullOrEmpty(s)) break;
                              lStreamWriter.WriteLine(s);
                           }
                           catch (System.IO.IOException) {
                              if (_ExitLoop) Console.WriteLine("CLIENT -> User requested shutdown.");
                              else Console.WriteLine("CLIENT -> disconnected");
                           }
                           catch (Exception ex) { Console.WriteLine(ex.Message); }
                        }
                     }
                     _ExitLoop = true;
                     Console.WriteLine(Environment.NewLine + "CLIENT -> shutting down");
                  }
               }
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
         } //

      } // class


      public static void Test() {
         NetworkListener lServer = new NetworkListener("127.0.0.1", 65432, "Server");
         NetworkClient lClient = new NetworkClient("127.0.0.1", 65432, "Client");

         lServer.WaitForClientsAsync();

         lClient.ConnectAsync();
         lClient.Send("Hello World!"); // send a text message across the network

         System.Threading.Thread.Sleep(1000);

         lClient.Disconnect();
         lServer.Disconnect();

         Console.ReadLine();
      } //

   } // class
} // namespace

example output:
SERVER MESSAGE -> Waiting for client number 0
SERVER MESSAGE -> 127.0.0.1:65432 new client connected.
SERVER MESSAGE -> Waiting for client number 1
SERVER MESSAGE -> 127.0.0.1:65432 Client said: Hello World!

CLIENT -> shutting down
SERVER MESSAGE -> 127.0.0.1:65432 Listener is shutting down
A blocking operation was interrupted by a call to WSACancelBlockingCall

#pragma once

#include <iostream>
#include <QtNetwork>
#include <QObject>
#include <QString>
#include <QTcpSocket>
#include <string>

using namespace std;

class Client : QObject {
    Q_OBJECT

private:
  int _Port = 0;
  QString _IpAddress;
  QTcpSocket _TcpSocket;

private slots:
    void Connected() { cout << "CLIENT MESSAGE -> connection established " << _IpAddress.toStdString() << ":" << _Port << endl; }

public:
   inline int getPort() { return _Port; }
   inline QString getIpAddress() { return _IpAddress; }

   Client(QString xIpAddress, int xPort) : QObject() {
    _IpAddress = xIpAddress;
    _Port = xPort;
   }
   ~Client() { cout << "deconstructing Client class " << _IpAddress.toStdString() << endl; }
   Client(QObject* lParent): QObject(lParent) { cout << "Client :)" << endl; }

  void Connect() {
      connect(&_TcpSocket, SIGNAL(connected()), this, SLOT(Connected()));
      QHostAddress lHostAddress(_IpAddress);
      _TcpSocket.connectToHost(lHostAddress, _Port);
      while (!_TcpSocket.waitForConnected( 2000 )) {
        cout << "CLIENT MESSAGE -> waiting for a feedback" << endl;
      }
  }

  void Disconnect() {
    _TcpSocket.close();
  } //

  void Send(const string &xText) {
    if (xText == "") return;
    const char *lText = xText.c_str();
    _TcpSocket.write(lText, xText.length() + 1);
  } //

};

//----------------------------------------------------------------------------------------------------------------------

#pragma once

#include <QTcpSocket>
#include <QHostAddress>
#include <thread>
#include <set>
#include <iostream>
#include <QtNetwork>

using namespace std;

class Connection : QObject {
    Q_OBJECT

  private:
    QTcpSocket *_TcpSocket;
    string _IpAddress;

    void Read() {
      try {
        char lBuffer[1024] = {0};
        _TcpSocket->read(lBuffer, _TcpSocket->bytesAvailable());
        cout << "SERVER MESSAGE -> " << _IpAddress << " Client said: " << lBuffer << endl;
      }
        catch (exception &ex) { cout << ex.what() << endl; }
    } //


  public:
    const string &IpAddress() const { return _IpAddress; }

    void Disconnect() {
      _TcpSocket->close();
      delete _TcpSocket;
    } //

    Connection(QTcpSocket *xTcpSocket) : _TcpSocket(xTcpSocket) {
      QHostAddress lAddress = xTcpSocket->localAddress();
      QString s = lAddress.toString();
      _IpAddress = s.toLocal8Bit().constData();
      connect(xTcpSocket, SIGNAL(readyRead()), this, SLOT(Read()));  // setup event
      cout << "SERVER MESSAGE -> " + _IpAddress << " new client connected." << endl;
    } //
    Connection(QObject* lParent): QObject(lParent) { cout << "Connection :)"; }
    ~Connection() { Disconnect(); cout << "deconstructing Connection class " << _IpAddress << endl; }

 }; // class

//----------------------------------------------------------------------------------------------------------------------

#pragma once

#include <iostream>
#include <QtNetwork>
#include <QObject>
#include <QString>
#include <QTcpSocket>
#include <QTcpServer>
#include <string>
#include "Connection.cpp"
#include <mutex>


using namespace std;

class Server : QObject {
    Q_OBJECT

private:
    list<Connection*> _AllClients;
    mutex mutex_AllClients;
    int _Port;
    string _IpAddress;
    QTcpServer _TcpServer;

    void OnClientShutDown(Connection *xClient) {
        cout << "SERVER MESSAGE -> Connection shutting down" << endl;
        mutex_AllClients.lock();
        _AllClients.remove(xClient);
        mutex_AllClients.unlock();
        xClient->Disconnect();
    } //

private slots:
    void NewConnection() {
        cout << "SERVER MESSAGE -> new connection" << endl;
        QTcpSocket *lSocket = _TcpServer.nextPendingConnection();
        Connection *lClient = new Connection(lSocket);
        mutex_AllClients.lock();
        _AllClients.push_back(lClient);
        mutex_AllClients.unlock();
    } //

public:    
    Server(QObject* lParent): QObject(lParent) { cout << "Server :)"; }
    ~Server() { cout << "deconstructing Server class " << endl; }
    inline int getPort() { return _Port; }
    inline string getIpAddress() { return _IpAddress; }    

    Server(string xIpAddress, int xPort) : QObject() {
      _Port = xPort;
      _IpAddress = xIpAddress;
      connect(&_TcpServer, SIGNAL(newConnection()), this, SLOT(NewConnection())); // setup event
      QString lIpAddress(xIpAddress.c_str());
      //QHostAddress lHostAddress(lIpAddress);
      if (_TcpServer.listen(QHostAddress::Any, xPort)) {
      //if(_TcpServer.listen(lHostAddress, xPort)) {
        cout << "SERVER MESSAGE -> listener up and running" << endl;
      }
    } //

    void Disconnect() {
      mutex_AllClients.lock();
      list<Connection*> lClone;
      list<Connection*>::const_iterator lIterator;
      for (lIterator = _AllClients.begin(); lIterator != _AllClients.end(); ++lIterator) {
        lClone.push_back(*lIterator);  // we need a clone, because the _AllClients collection will be manipulated during the foreach loop
      }
      mutex_AllClients.unlock();

      for (Connection *lClient : lClone) lClient->Disconnect();
      _TcpServer.close();
    } //

}; // class

//----------------------------------------------------------------------------------------------------------------------

#include <QApplication>
#include <iostream>
#include <thread>
#include "Server.h"
#include "Client.h"

int main(int argc, char** argv)
{
  QApplication app(argc, argv);
  try {

    Server lServer("127.0.0.1", 65432);
    Client lClient("127.0.0.1", 65432);

    lClient.Connect();    

    this_thread::sleep_for(chrono::milliseconds(1000)) ;
    lClient.Send("Hello World!"); // send a text message across the network
    this_thread::sleep_for(chrono::milliseconds(1000)) ;

    lClient.Disconnect();
    lServer.Disconnect();

    cout << "press enter to exit" << endl;
    cin.get();
  }
  catch (exception &ex) { cerr << ex.what() << endl; }
  return app.exec();
} //
package DemoApp;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public final class Server extends Thread {

  private final ArrayList<Client> _Clients = new ArrayList<>();
  public final int port;
  ServerSocket _ServerSocket;

  public Server(String xName, int xPort) throws IOException {
    super(xName);
    port = xPort;
    setDaemon(true);
    _ServerSocket = new ServerSocket(xPort);
  } // constructor

  public void close() throws IOException {
    this.interrupt(); // exit loop
    synchronized (_Clients) {
      for (Client lClient : _Clients) lClient.close();
      _Clients.clear();
    }

    _ServerSocket.close();
    System.out.println(_ServerSocket.getLocalSocketAddress().toString() + " server closed down");
  } //

  public void broadcast(String xMessage) {
    synchronized (_Clients) {
      for (Client lClient : _Clients)
        lClient.getWriter().send(xMessage);
    }
  } //

  @Override
  public void run() {
    System.out.println(_ServerSocket.getLocalSocketAddress().toString() + " server is waiting for clients");

    try {
      while (true) {
        Socket lSocket = _ServerSocket.accept();  // wait for a client to connect
        Client lClient = new Client(lSocket);
        synchronized (_Clients) {
          _Clients.add(lClient);
        }
        lClient.connect();
      }
    } catch (IOException ex) {
      //System.out.println(ex.getMessage());
    }
  } //

} // class

// -----------------------------------------------------------------------------------------------------------------------

package DemoApp;

import java.io.IOException;
import java.net.Socket;

public class Client {

  private Socket _Socket = null;
  public final String IpAddressHost;
  public final String IpAddressLocal;
  public final String IpConnectionString;
  private Reader _Reader = null;
  private Writer _Writer = null;

  public Socket getSocket() {
    return _Socket;
  } //

  public Reader getReader() {
    return _Reader;
  } //

  public Writer getWriter() {
    return _Writer;
  } //

  public void close() throws IOException {
    synchronized (this) {
      if (_Reader != null) _Reader.close(true);
      _Reader = null;
      if (_Writer != null) _Writer.close(true);
      _Writer = null;
    }

    System.out.println(IpConnectionString + " client closed down");
  } //

  public Client(Socket xSocket) {
    _Socket = xSocket;
    IpAddressHost = xSocket.getInetAddress().getHostAddress() + ":" + _Socket.getPort();
    IpAddressLocal = xSocket.getLocalAddress().getHostAddress() + ":" + _Socket.getLocalPort();
    IpConnectionString = "(Local " + IpAddressLocal + ",  Host " + IpAddressHost + ")";
  } //

  public void connect() throws IOException {
    _Reader = new Reader(this);
    _Writer = new Writer(this);
    _Reader.start();  // start listening
    _Writer.start();  // start write loop        
  } //  

} // class

// -----------------------------------------------------------------------------------------------------------------------

package DemoApp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Reader extends Thread {

  private final Client _Client;
  private BufferedReader _StreamReader;

  public Reader(Client xClient) throws IOException {
    _Client = xClient;
    Socket lSocket = xClient.getSocket();
    _StreamReader = new BufferedReader(new InputStreamReader(lSocket.getInputStream()));
  } // constructor

  public void close(boolean xExitLoop) throws IOException {
    synchronized (this) {
      if (xExitLoop) interrupt();
      if (_StreamReader == null) return;
      _StreamReader.close();
      _StreamReader = null;
    }

    System.out.println(_Client.IpConnectionString + " closed reader connection");
  } //

  @Override
  public void run() {
    System.out.println(_Client.IpConnectionString + " start new reader connection");

    while (true)
      try {
        String lInput = _StreamReader.readLine();
        if (lInput == null) break;
        if (lInput.isEmpty()) continue;

        System.out.println(_Client.IpConnectionString + " message received: " + lInput);
        if ("EXIT".equals(lInput)) break;

      } catch (IOException | RuntimeException ex) {
        System.out.println(ex);
      }
    try {
      close(false);
    } catch (IOException ex) {
    }
  } //

} // class

// -----------------------------------------------------------------------------------------------------------------------

package DemoApp;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.LinkedList;

public class Writer extends Thread {

  private final Client _Client;
  private PrintWriter _PrintWriter;

  private final LinkedList<String> _WriteQueue = new LinkedList<>();

  public Writer(Client xClient) throws IOException {
    setDaemon(true);
    _Client = xClient;
    Socket lSocket = xClient.getSocket();
    _PrintWriter = new PrintWriter(lSocket.getOutputStream(), true);
  } // constructor

  public void close(boolean xExitLoop) throws IOException {
    synchronized (this) {
      if (xExitLoop) interrupt();
      if (_PrintWriter == null) return;
      _PrintWriter.close();
      _PrintWriter = null;
    }
    System.out.println(_Client.IpConnectionString + " closed writer connection");
  } //

  public void send(String xText) {
    if (xText == null) return;

    synchronized (_WriteQueue) {
      _WriteQueue.addLast(xText);
      _WriteQueue.notify();
    }
  } //

  @Override
  public void run() {
    System.out.println(_Client.IpConnectionString + " start new writer connection");

    try {
      while (true) {
        String lText = null;
        synchronized (_WriteQueue) {
          if (_WriteQueue.size() > 0) lText = _WriteQueue.removeFirst();
          else _WriteQueue.wait();
        }
        if (lText == null) continue;
        if (_PrintWriter == null) continue;
        _PrintWriter.println(lText);
      }
    } catch (InterruptedException ex) {
      if (ex.getMessage() != null) System.out.println(ex.getMessage());
    }
    try {
      close(false);
    } catch (IOException ex) {
    }
  } //

} // class

// -----------------------------------------------------------------------------------------------------------------------

package DemoApp;

import java.io.IOException;
import java.net.Socket;

public class Test {

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

  public static void main(String[] args) throws IOException {
    Server lServer = new Server("MyServer", 65432);
    lServer.start();
    Sleep();

    Socket lClientSocket1 = new Socket("localhost", 65432);
    Client lClient1 = new Client(lClientSocket1);
    lClient1.connect();
    Sleep();

    Socket lClientSocket2 = new Socket("localhost", 65432);
    Client lClient2 = new Client(lClientSocket2);
    lClient2.connect();
    Sleep();

    lClient1.getWriter().send("client->server: hello server!");
    Sleep();

    lServer.broadcast("server->client: hello client!");
    Sleep();

    lClient1.getWriter().send("EXIT");
    lClient1.close();

    lClient2.getWriter().send("EXIT");
    lClient2.close();

    Sleep();

    lServer.close();
    //System.in.read();
    Sleep();
  }
} // class

example output:
0.0.0.0/0.0.0.0:65432 server is waiting for clients
(Local 127.0.0.1:65432, Host 127.0.0.1:54059) start new reader connection
(Local 127.0.0.1:54059, Host 127.0.0.1:65432) start new reader connection
(Local 127.0.0.1:65432, Host 127.0.0.1:54059) start new writer connection
(Local 127.0.0.1:54059, Host 127.0.0.1:65432) start new writer connection
(Local 127.0.0.1:54060, Host 127.0.0.1:65432) start new reader connection
(Local 127.0.0.1:54060, Host 127.0.0.1:65432) start new writer connection
(Local 127.0.0.1:65432, Host 127.0.0.1:54060) start new reader connection
(Local 127.0.0.1:65432, Host 127.0.0.1:54060) start new writer connection
(Local 127.0.0.1:65432, Host 127.0.0.1:54059) message received: client->server: hello server!
(Local 127.0.0.1:54059, Host 127.0.0.1:65432) message received: server->client: hello client!
(Local 127.0.0.1:54060, Host 127.0.0.1:65432) message received: server->client: hello client!
(Local 127.0.0.1:65432, Host 127.0.0.1:54059) message received: EXIT
(Local 127.0.0.1:65432, Host 127.0.0.1:54059) closed reader connection
(Local 127.0.0.1:54059, Host 127.0.0.1:65432) closed reader connection
(Local 127.0.0.1:54059, Host 127.0.0.1:65432) closed writer connection
(Local 127.0.0.1:54059, Host 127.0.0.1:65432) client closed down
(Local 127.0.0.1:65432, Host 127.0.0.1:54060) message received: EXIT
(Local 127.0.0.1:65432, Host 127.0.0.1:54060) closed reader connection
(Local 127.0.0.1:54060, Host 127.0.0.1:65432) closed reader connection
(Local 127.0.0.1:54060, Host 127.0.0.1:65432) closed writer connection
(Local 127.0.0.1:54060, Host 127.0.0.1:65432) client closed down
(Local 127.0.0.1:65432, Host 127.0.0.1:54059) closed writer connection
(Local 127.0.0.1:65432, Host 127.0.0.1:54059) client closed down
(Local 127.0.0.1:65432, Host 127.0.0.1:54060) closed writer connection
(Local 127.0.0.1:65432, Host 127.0.0.1:54060) client closed down
0.0.0.0/0.0.0.0:65432 server closed down

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Tcp {
   public class Server {

      private readonly List<Client> _Clients = new List<Client>();
      public readonly int Port;
      private TcpListener _TcpListener = null;
      private Thread _Thread;
      private readonly string Name;

      public Server(String xName, int xPort) {
         Name = xName;
         Port = xPort;
      } // constructor

      public bool start() {
         try {
            _TcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), Port);
            _TcpListener.Start();

            _Thread = new Thread(loop);
            _Thread.IsBackground = true;
            _Thread.Name = Name;
            _Thread.Start();
         }
         catch (Exception ex) { Console.WriteLine(ex.StackTrace + Environment.NewLine + ex.Message); return false; }
         return true;
      } //

      public void Close() {
         if (_Thread != null) { _Thread.Interrupt(); _Thread = null; }
         lock (_Clients) {
            foreach (Client lClient in _Clients) lClient.close();
            _Clients.Clear();
         }

         _TcpListener.Stop();
         Console.WriteLine(_TcpListener.LocalEndpoint.ToString() + " server closed down");
         _TcpListener = null;
      } //

      public void broadcast(String xMessage) {
         lock (_Clients) {
            foreach (Client lClient in _Clients) lClient.Writer.Send(xMessage);
         }
      } //

      private void loop() {
         Console.WriteLine(_TcpListener.LocalEndpoint.ToString() + " server is waiting for clients");

         try {
            while (true) {
               int lClientCount;
               lock (_Clients) lClientCount = _Clients.Count;
               TcpClient lTcpClient = _TcpListener.AcceptTcpClient();   // wait for a client to connect
               Client lClient = new Client(lTcpClient);
               lock (_Clients) _Clients.Add(lClient);
               lClient.Connect();
            }
         }
         catch (ThreadInterruptedException) { } // Thread interruption
         catch (SocketException) { } // Thread interruption => SocketException
         catch (Exception ex) { Console.WriteLine(ex.StackTrace + Environment.NewLine + ex.Message); }
      } //

   } // class
} // namespace

// -----------------------------------------------------------------------------------------------------------------------

using System;
using System.Net.Sockets;

namespace Tcp {
   public class Client {
      public TcpClient TcpClient { get; private set; }
      public readonly String IpAddressHost;
      public readonly String IpAddressLocal;
      public readonly String IpConnectionString;
      public Reader Reader { get; private set; }
      public Writer Writer { get; private set; }

      public void close() {
         lock (this) {
            if (Reader != null) Reader.Close(true);
            Reader = null;
            if (Writer != null) Writer.Close(true);
            Writer = null;
            if (TcpClient != null) {
               TcpClient.Close();
               Console.WriteLine(IpConnectionString + " TcpClient read/write closed");
            }
            TcpClient = null;
         }

         Console.WriteLine(IpConnectionString + " client closed down");
      } //

      public Client(string xHostname, int xPort) : this(new TcpClient(xHostname, xPort)) { }   // create and connect

      public Client(TcpClient xTcpClient) {
         TcpClient = xTcpClient;
         IpAddressHost = TcpClient.Client.RemoteEndPoint.ToString();
         IpAddressLocal = TcpClient.Client.LocalEndPoint.ToString();
         IpConnectionString = "(Local " + IpAddressLocal + ",  Host " + IpAddressHost + ")";
      } //

      public void Connect() {
         Reader = new Reader(this);
         Writer = new Writer(this);
         Reader.Start();  // start listening
         Writer.Start();  // start write loop        
      } //  

   } // class
} // namespace

// -----------------------------------------------------------------------------------------------------------------------

using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;

namespace Tcp {
   public class Reader {

      private readonly Client _Client;
      private StreamReader _StreamReader = null;
      private Thread _Thread = null;

      public Reader(Client xClient) {
         _Client = xClient;
         NetworkStream lNetworkStream = xClient.TcpClient.GetStream();
         _StreamReader = new StreamReader(lNetworkStream);
      } // constructor

      public void Start() {
         _Thread = new Thread(loop);
         _Thread.IsBackground = true;
         _Thread.Start();
      } //

      public void Close(bool xExitLoop) {
         lock (this) {
            if (xExitLoop && (_Thread != null)) { _Thread.Interrupt(); _Thread = null; }

            // _Client.TcpClient.Close() in Client object
            //if (_StreamReader == null) return;
            //_StreamReader.Close();
            //_StreamReader = null;
         }
         
         //Console.WriteLine(_Client.IpConnectionString + " closed reader connection");
      } //

      private void loop() {
         Console.WriteLine(_Client.IpConnectionString + " start new reader connection");

         while (true) {
            try {
               String lInput = _StreamReader.ReadLine();
               if (lInput == null) break;
               if (lInput == "") continue;

               Console.WriteLine(_Client.IpConnectionString + " message received: " + lInput);
               if ("EXIT".Equals(lInput)) break;

            }
            catch (ThreadInterruptedException) { break; } // Thread interruption
            catch (IOException) { break; } // StreamReader disposed
            catch (Exception ex) { Console.WriteLine(ex.StackTrace + Environment.NewLine + ex.Message); }
         }
         Close(false);
      } //

   } // class
} // namespace

// -----------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.Sockets;
using System.Threading;

namespace Tcp {
   public class Writer {

      private readonly Client _Client;
      private StreamWriter _StreamWriter = null;
      private Thread _Thread = null;

      private readonly BlockingCollection<String> _WriteQueue = new BlockingCollection<string>();

      public Writer(Client xClient) {
         _Client = xClient;
         NetworkStream lNetworkStream = xClient.TcpClient.GetStream();
         _StreamWriter = new StreamWriter(lNetworkStream);
      } // constructor

      public void Start() {
         _Thread = new Thread(Loop);
         _Thread.IsBackground = true;
         _Thread.Start();
      } //

      public void Close(bool xExitLoop) {
         lock (this) {
            if (xExitLoop && (_Thread != null)) { _Thread.Interrupt(); _Thread = null; }

            // _Client.TcpClient.Close() in Client object
            //if (_StreamWriter == null) return;
            //_StreamWriter.Close();
            //_StreamWriter = null;
         }
         //Console.WriteLine(_Client.IpConnectionString + " closed writer connection");
      } //

      public void Send(string xText) {
         if (string.IsNullOrEmpty(xText)) return;
         _WriteQueue.Add(xText);
      } //


      private void Loop() {
         Console.WriteLine(_Client.IpConnectionString + " start new writer connection");

         while (true) {
            try {
               string lText = null;
               lText = _WriteQueue.Take();
               if (string.IsNullOrEmpty(lText)) continue;
               if (_StreamWriter == null) continue;
               _StreamWriter.WriteLine(lText);
               _StreamWriter.Flush();
            }
            catch (ObjectDisposedException) { break; } 
            catch (ThreadInterruptedException) { break; } // Thread interruption
            catch (Exception ex) { Console.WriteLine(ex.StackTrace + Environment.NewLine + ex.Message); }
         }

         Close(false);
      } //


   } // class
} // namespace

// -----------------------------------------------------------------------------------------------------------------------

using System.Net.Sockets;
using System.Threading;

namespace Tcp {
   class Program {
      static void Main(string[] args) {

         Server lServer = new Server("MyServer", 65432);
         lServer.start();
         Thread.Sleep(2000);

         TcpClient lClientSocket1 = new TcpClient("localhost", 65432);
         Client lClient1 = new Client(lClientSocket1);
         lClient1.Connect();
         Thread.Sleep(2000);

         TcpClient lClientSocket2 = new TcpClient("localhost", 65432);
         Client lClient2 = new Client(lClientSocket2);
         lClient2.Connect();
         Thread.Sleep(2000);

         lClient1.Writer.Send("client->server: hello server!");
         Thread.Sleep(2000);

         lServer.broadcast("server->client: hello client!");
         Thread.Sleep(2000);

         lClient1.Writer.Send("EXIT");
         Thread.Sleep(2000);
         lClient1.close();

         lClient2.Writer.Send("EXIT");
         Thread.Sleep(2000);
         lClient2.close();

         Thread.Sleep(1000);

         lServer.Close();
         //Console.ReadLine();
         Thread.Sleep(5000);

      } // main
   } // class
} // namespace

example output:
127.0.0.1:65432 server is waiting for clients
(Local 127.0.0.1:65432, Host 127.0.0.1:58161) start new reader connection
(Local 127.0.0.1:58161, Host 127.0.0.1:65432) start new reader connection
(Local 127.0.0.1:58161, Host 127.0.0.1:65432) start new writer connection
(Local 127.0.0.1:65432, Host 127.0.0.1:58161) start new writer connection
(Local 127.0.0.1:65432, Host 127.0.0.1:58163) start new reader connection
(Local 127.0.0.1:58163, Host 127.0.0.1:65432) start new reader connection
(Local 127.0.0.1:65432, Host 127.0.0.1:58163) start new writer connection
(Local 127.0.0.1:58163, Host 127.0.0.1:65432) start new writer connection
(Local 127.0.0.1:65432, Host 127.0.0.1:58161) message received: client->server: hello server!
(Local 127.0.0.1:58161, Host 127.0.0.1:65432) message received: server->client: hello client!
(Local 127.0.0.1:58163, Host 127.0.0.1:65432) message received: server->client: hello client!
(Local 127.0.0.1:65432, Host 127.0.0.1:58161) message received: EXIT
(Local 127.0.0.1:58161, Host 127.0.0.1:65432) TcpClient read/write closed
(Local 127.0.0.1:58161, Host 127.0.0.1:65432) client closed down
(Local 127.0.0.1:65432, Host 127.0.0.1:58163) message received: EXIT
(Local 127.0.0.1:58163, Host 127.0.0.1:65432) TcpClient read/write closed
(Local 127.0.0.1:58163, Host 127.0.0.1:65432) client closed down
(Local 127.0.0.1:65432, Host 127.0.0.1:58161) TcpClient read/write closed
(Local 127.0.0.1:65432, Host 127.0.0.1:58161) client closed down
(Local 127.0.0.1:65432, Host 127.0.0.1:58163) TcpClient read/write closed
(Local 127.0.0.1:65432, Host 127.0.0.1:58163) client closed down
127.0.0.1:65432 server closed down