Blog Archives
migration Java, C++ (day 15), professional), call C++ from Java, call Java from C++
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.
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
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!
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.
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)
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.
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!
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:
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 TangoC++: 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.
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 foundHashCode 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 foundHashCode 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 foundHashCode 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.
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()
Thread1Thread1 result is: 246
Thread2, f1.get()
Thread2Thread2, f2.get()
Thread2
Thread2 deferred result is: 246
Thread2, f3.get()
Thread2Thread2 async result is: 246
Thread2
DoSomething()
Thread3lPromise5.set_value()
f5b async result is: 246
DoSomething()
Thread4
Thread4Thread4
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 20141/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
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: 5moving disk 1 from 0 to 2
stack0: 5 4 3 2
stack1:
stack2: 1moving disk 2 from 0 to 1
stack0: 5 4 3
stack1: 2
stack2: 1moving 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: 3moving disk 1 from 1 to 0
stack0: 5 4 1
stack1: 2
stack2: 3moving disk 2 from 1 to 2
stack0: 5 4 1
stack1:
stack2: 3 2moving disk 1 from 0 to 2
stack0: 5 4
stack1:
stack2: 3 2 1moving disk 4 from 0 to 1
stack0: 5
stack1: 4
stack2: 3 2 1moving disk 1 from 2 to 1
stack0: 5
stack1: 4 1
stack2: 3 2moving disk 2 from 2 to 0
stack0: 5 2
stack1: 4 1
stack2: 3moving disk 1 from 1 to 0
stack0: 5 2 1
stack1: 4
stack2: 3moving 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: 1moving disk 2 from 0 to 1
stack0: 5
stack1: 4 3 2
stack2: 1moving 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: 5moving disk 1 from 1 to 0
stack0: 1
stack1: 4 3 2
stack2: 5moving disk 2 from 1 to 2
stack0: 1
stack1: 4 3
stack2: 5 2moving disk 1 from 0 to 2
stack0:
stack1: 4 3
stack2: 5 2 1moving disk 3 from 1 to 0
stack0: 3
stack1: 4
stack2: 5 2 1moving disk 1 from 2 to 1
stack0: 3
stack1: 4 1
stack2: 5 2moving disk 2 from 2 to 0
stack0: 3 2
stack1: 4 1
stack2: 5moving disk 1 from 1 to 0
stack0: 3 2 1
stack1: 4
stack2: 5moving disk 4 from 1 to 2
stack0: 3 2 1
stack1:
stack2: 5 4moving disk 1 from 0 to 2
stack0: 3 2
stack1:
stack2: 5 4 1moving disk 2 from 0 to 1
stack0: 3
stack1: 2
stack2: 5 4 1moving disk 1 from 2 to 1
stack0: 3
stack1: 2 1
stack2: 5 4moving disk 3 from 0 to 2
stack0:
stack1: 2 1
stack2: 5 4 3moving disk 1 from 1 to 0
stack0: 1
stack1: 2
stack2: 5 4 3moving disk 2 from 1 to 2
stack0: 1
stack1:
stack2: 5 4 3 2moving disk 1 from 0 to 2
stack0:
stack1:
stack2: 5 4 3 2 1
migration C#, Java, C++ (day 9b), TextIO, Tuples
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
migration C#, Java, C++ (day 7), TBB
This is some relaxing stuff today.
Recommended reading for variable parameters on the C# side: __arglist in the Print() method
variable parameters
double avg(int xCount, params double[] xDoubles) { double lSum = 0; foreach (double d in xDoubles) lSum += d; return lSum / xDoubles.Length; } //
private double avg(Double... xDoubles) { double lSum = 0; for (double d : xDoubles) lSum += d; return lSum / xDoubles.length; } //
// #include <cstdarg> double avg(int xCount, ...) { va_list args; double lSum = 0; va_start(args, xCount); for (int i = 0; i < xCount; i++) lSum += va_arg(args, double); va_end(args); return lSum / xCount; }
Parallel vs. TBB
public void DoSomething() { Console.Write(Thread.CurrentThread.ManagedThreadId + " "); Thread.Sleep(100); } // Stopwatch _Stopwatch = new Stopwatch(); void startTimer() { _Stopwatch.Reset(); _Stopwatch.Start(); } // void stopTimer() { long h = _Stopwatch.ElapsedMilliseconds; Console.WriteLine("timer millisecs: " + h); _Stopwatch.Restart(); } // BlockingCollection<int> _Queue; void Producer() { Console.WriteLine(); for (int i = 10; i >= 0; i--) { DoSomething(); if (!_Queue.TryAdd(i)) Console.WriteLine("failed to push a new value to the queue"); } } // void TBBs() { const int lSize = 50; startTimer(); for (int i = 0; i < lSize; i++) DoSomething(); stopTimer(); Parallel.For(0, lSize, x => DoSomething()); stopTimer(); List<int> lList = new List<int>(); for (int i = 0; i < lSize; i++) lList.Add(i); startTimer(); Parallel.ForEach(lList, x => DoSomething()); stopTimer(); Parallel.Invoke(DoSomething, DoSomething, DoSomething); stopTimer(); Thread lThread = new Thread(Producer); lThread.Start(); using (_Queue = new BlockingCollection<int>()) { while (true) { int x = _Queue.Take(); Console.WriteLine("received value " + x); if (x == 0) break; } } } //
public final void DoSomething() { System.out.print(Thread.currentThread().getId() + " "); try { Thread.sleep(100); } catch (InterruptedException ex) { System.out.println(ex.getMessage()); } } public class Stopwatch { long _Time; public void startTimer() { _Time = System.currentTimeMillis(); } // public void stopTimer() { long h = System.currentTimeMillis(); System.out.println("timer millisecs: " + (h - _Time)); _Time = h; } // } // class private final LinkedBlockingDeque<Integer> _Queue = new LinkedBlockingDeque<>(); private void Producer() { System.out.println(); for (int i = 10; i >= 0; i--) { DoSomething(); if (!_Queue.add(i)) System.out.println("failed to push a new value to the queue"); } } private void TBBs() { final int lSize = 50; Stopwatch lStopwatch = new Stopwatch(); lStopwatch.startTimer(); for (int i = 0; i < lSize; i++) DoSomething(); lStopwatch.stopTimer(); int lCPUs = Runtime.getRuntime().availableProcessors(); ExecutorService lExecutor = Executors.newFixedThreadPool(lCPUs); for (int i = 0; i < lSize; i++) lExecutor.submit(() -> DoSomething()); lStopwatch.stopTimer(); java.util.ArrayList<Integer> lList = new java.util.ArrayList<>(); for (int i = 0; i < lSize; i++) lList.add(i); lStopwatch.startTimer(); lList.parallelStream().forEach(x -> DoSomething()); lStopwatch.stopTimer(); Callable<Void> lCallable = () -> { DoSomething(); return null; }; ArrayList<Callable<Void>> lCallables = new ArrayList<>(); lCallables.add(lCallable); lCallables.add(lCallable); lCallables.add(lCallable); try { lExecutor.invokeAll(lCallables); } catch (InterruptedException ex) { System.out.println("InterruptedException: " + ex.getMessage()); } lStopwatch.stopTimer(); Thread lThread = new Thread(() -> { Producer(); }); lThread.start(); while (true) { int x = 0; try { x = _Queue.take(); } catch (InterruptedException ex) { System.out.println("InterruptedException: " + ex.getMessage()); } System.out.println("received value " + x); if (x == 0) break; } } //
// #include <thread> void DoSomething() { cout << this_thread::get_id() << " "; this_thread::sleep_for(chrono::milliseconds(100)); } // unsigned int _timer = 0; // #include <ctime> void startTimer() { _timer = clock(); } void stopTimer() { unsigned int lTimer = clock(); cout << endl << "timer millisecs: " << clock() - _timer << endl; _timer = lTimer; } tbb::concurrent_bounded_queue<int> *_Queue; void Producer() { cout << endl; for (int i = 10; i >= 0; i--) { DoSomething(); if (!_Queue->try_push(i)) cout << "failed to push a new value to the queue" << endl; } } // #include <thread> void TBBs() { const int lSize = 50; startTimer(); for (int i = 0; i < lSize; i++) DoSomething(); stopTimer(); tbb::parallel_for(0, lSize, 1, [&](int i) { DoSomething(); }); stopTimer(); vector<int> lVector; for (int i = 0; i < lSize; i++) lVector.push_back(i); startTimer(); tbb::parallel_for_each(lVector.begin(), lVector.end(), [](int i) { DoSomething(); }); stopTimer(); tbb::parallel_do(lVector.begin(), lVector.end(), [](int i) { DoSomething(); }); stopTimer(); tbb::parallel_invoke(DoSomething, DoSomething, DoSomething); _Queue = new tbb::concurrent_bounded_queue<int>(); thread lThread(Producer); lThread.detach(); int i; while (true) { _Queue->pop(i); cout << endl << "received value " << i << endl; if (i == 0) break; } delete _Queue; _Queue = nullptr; } //
delegates/Action/Func vs. func
void F1(string s) { Console.WriteLine(s); } void F2(string s) { Console.WriteLine(">" + s + "<"); } public void test() { // delegates Action<string> f1 = F1; // use Func<> for methods with return values f1("hello world"); F1("hello world"); Action<string> f3 = (s => f1(s)); f3("hello echo"); f1 = F2; f1("hello world"); // variable argument list Console.WriteLine("average is " + avg(4, 1.1, 2.2, 3.3, 4.4)); // 2.75 TBBs(); } //
private void F1(String s) { System.out.println(s); } private void F2(String s) { System.out.println(">" + s + "<"); } public interface Action<T> { void invoke(T t); } public void test() { // there are neither delegates nor pointers in Java Action<String> f1 = s -> F1(s); f1.invoke("hello world"); F1("hello world"); Action<String> f3 = s -> f1.invoke(s); f3.invoke("hello echo"); //f1 = s -> F2(s); compiler error, becuase this would change the "effectively final" status of f1 Action<String> f4 = f1; // simple assignment f4 = s -> F2(s); f4.invoke("hello world"); // variable argument list System.out.println("average is " + avg(1.1, 2.2, 3.3, 4.4)); // 2.75 TBBs(); } //
void F1(const string &s) { cout << s << endl; } void F2(const string &s) { cout << ">" << s << "<" << endl; } void test() { // delegates function<void(const string &)> f1 = F1; f1("hello world"); F1("hello world"); function<void(const string &)> f3 = [=](const string &s) { f1(s); }; f3("hello echo"); f1 = F2; f1("hello world"); // variable argument list cout << "average is " << avg(4, 1.1, 2.2, 3.3, 4.4) << endl; // 2.75 TBBs(); } //
today’s requirements
using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Diagnostics; using System.Threading; using System.Threading.Tasks;
#include "tbb/tbb.h" #include "tbb/parallel_for.h" #include "tbb/concurrent_queue.h" #include "tbb/blocked_range.h" #include <cstdarg> #include <thread> #include <iostream> #include <string> #include <ctime>