Blog Archives
migration C#, Java, C++ (day 6), MySQL
It is nearly midnight now. 7 minutes are left. Phew, I made it. Good night.
Discipline or stupidity? I guess both 😉
Was fun though. I am achieving my targets.
But now I need something soft, no more hardcore today. Maybe a cushion … Zzzzzzz
MySQL
private void ExecuteNonQuery(MySqlConnection xConnection, string xCommand) { MySqlCommand lCommand = xConnection.CreateCommand(); lCommand.CommandText = xCommand; lCommand.ExecuteNonQuery(); } // private void ExecuteNonQueryAddRow(MySqlConnection xConnection, string xName, DateTime xBirthday) { MySqlCommand lCommand = xConnection.CreateCommand(); lCommand.CommandText = "INSERT INTO test (name, birthdate) values (@name, @birthdate)"; MySqlParameterCollection lParams = lCommand.Parameters; lParams.AddWithValue("@name", xName); lParams.AddWithValue("@birthdate", xBirthday); lCommand.ExecuteNonQuery(); } // private DataTable ExecuteQuery(MySqlConnection xConnection, string xCommand) { MySqlCommand lCommand = xConnection.CreateCommand(); lCommand.CommandText = xCommand; MySqlDataAdapter lAdapter = new MySqlDataAdapter(lCommand); DataSet lDataSet = new DataSet(); lAdapter.Fill(lDataSet); return lDataSet.Tables[0]; } // public void MySqlConnectionTest() { string lConnectionString = "Server=127.0.0.1;Database=ohta;Uid=root;Pwd=root;"; try { using (MySqlConnection lConnection = new MySqlConnection(lConnectionString)) { lConnection.Open(); //DataTable lSchema = lConnection.GetSchema(); // not required to get or set anything, see lConnectionString // create a new table string lField1 = "id INT NOT NULL AUTO_INCREMENT,"; string lField2 = "name VARCHAR(40) NOT NULL,"; string lField3 = "birthdate DATE,"; string lKey = "PRIMARY KEY (id)"; string lTableDefinition = lField1 + lField2 + lField3 + lKey; ExecuteNonQuery(lConnection, "DROP TABLE IF EXISTS test"); ExecuteNonQuery(lConnection, "CREATE TABLE test(" + lTableDefinition + ");"); // fill the table with values ExecuteNonQueryAddRow(lConnection, "George Washington", new DateTime(1732, 2, 22)); ExecuteNonQueryAddRow(lConnection, "Abraham Lincoln", new DateTime(1809, 2, 12)); ExecuteNonQueryAddRow(lConnection, "Ronald Reagan", new DateTime(1911, 2, 6)); ExecuteNonQueryAddRow(lConnection, "Bill Clinton", new DateTime(1946, 8, 19)); // Query DataTable lTable = ExecuteQuery(lConnection, "SELECT * FROM test"); // print column names foreach (DataColumn lColumn in lTable.Columns) Console.Write(lColumn.ColumnName + "; "); Console.WriteLine(); // print rows int lColumnCount = lTable.Columns.Count; foreach (DataRow lRow in lTable.Rows) { for (int i = 0; i < lColumnCount; i++) { object o = lRow[i]; if (o is DateTime) { DateTime lDateTime = (DateTime)o; Console.Write(lDateTime.ToString("dd MMM yyyy") + "; "); continue; } Console.Write(o.ToString() + "; "); } Console.WriteLine(); } // clean up ExecuteNonQuery(lConnection, "DROP TABLE IF EXISTS test"); } } catch (Exception ex) { Console.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace); } } //
package Demos; import java.sql.*; import java.util.*; public class MySqlDemo { public static Calendar getDate(int xYear, int xMonth, int xDay, int xHour, int xMinute, int xSecond) { Calendar lCalendar = Calendar.getInstance(); lCalendar.clear(); lCalendar.set(xYear, xMonth - 1, xDay, xHour, xMinute, xSecond); return lCalendar; } // private int ExecuteNonQuery(Connection xConnection, String xCommand) { Statement lStatement = null; try { lStatement = xConnection.createStatement(); return lStatement.executeUpdate(xCommand); } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } finally { CloseStatement(lStatement); } return -1; } // private void ExecuteNonQueryAddRow(Connection xConnection, String xName, Calendar xBirthday) { // old school //String lBirthday = String.format("%1$tY-%1$tm-%1$te", xBirthday); //String lCommand = String.format("INSERT INTO test (name, birthdate) values ('%s', '%s')", xName, lBirthday); //int lResult = ExecuteNonQuery(xConnection, lCommand); // modern school PreparedStatement lStatement = null; try { java.sql.Date lBirthday = new java.sql.Date(xBirthday.getTimeInMillis()); lStatement = xConnection.prepareStatement("INSERT INTO test (name, birthdate) VALUES (?,?)"); lStatement.setString(1, xName); lStatement.setDate(2, lBirthday, xBirthday); lStatement.executeUpdate(); } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } finally { CloseStatement(lStatement); } } // private void CloseStatement(Statement xStatement) { if (xStatement == null) return; try { xStatement.close(); } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } } // private void CloseStatement(PreparedStatement xStatement) { if (xStatement == null) return; try { xStatement.close(); } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } } // private void CloseResultSet(ResultSet xResultSet) { if (xResultSet == null) return; try { xResultSet.close(); } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } } // private void ExecuteQuery(Connection xConnection, String xCommand) { ResultSet lResultSet = null; Statement lStatement = null; try { lStatement = xConnection.createStatement(); lResultSet = lStatement.executeQuery(xCommand); // print column headers ResultSetMetaData lMetaData = lResultSet.getMetaData(); for (int i = 1, n = lMetaData.getColumnCount(); i <= n; i++) System.out.print(lMetaData.getColumnLabel(i) + " "); System.out.println(); // print data while (lResultSet.next()) { long lId = lResultSet.getLong(1); String lName = lResultSet.getString(2); java.sql.Date lBirthday = lResultSet.getDate(3); System.out.println(lId + ": " + lName + " was born on " + lBirthday.toString()); } } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } finally { CloseStatement(lStatement); CloseResultSet(lResultSet); } } // public final void MySqlConnectionTest() { String lIpAddress = "127.0.0.1:3306"; String lDatabase = "ohta"; String lUser = "root"; String lPassword = "root"; Connection lConnection = null; try { Class.forName("com.mysql.jdbc.Driver"); System.out.println("driver connection successful"); String lUrl = "jdbc:mysql://" + lIpAddress + "/" + lDatabase; lConnection = DriverManager.getConnection(lUrl, lUser, lPassword); System.out.println("database connection successful"); // create a new table String lField1 = "id INT NOT NULL AUTO_INCREMENT,"; String lField2 = "name VARCHAR(40) NOT NULL,"; String lField3 = "birthdate DATE,"; String lKey = "PRIMARY KEY (id)"; String lTableDefinition = lField1 + lField2 + lField3 + lKey; ExecuteNonQuery(lConnection, "DROP TABLE IF EXISTS test"); ExecuteNonQuery(lConnection, "CREATE TABLE test(" + lTableDefinition + ");"); // fill the table with values ExecuteNonQueryAddRow(lConnection, "George Washington", getDate(1732, 2, 22, 0, 0, 0)); ExecuteNonQueryAddRow(lConnection, "Abraham Lincoln", getDate(1809, 2, 12, 0, 0, 0)); ExecuteNonQueryAddRow(lConnection, "Ronald Reagan", getDate(1911, 2, 6, 0, 0, 0)); ExecuteNonQueryAddRow(lConnection, "Bill Clinton", getDate(1946, 8, 19, 0, 0, 0)); // Query ExecuteQuery(lConnection, "SELECT * FROM test"); // clean up ExecuteNonQuery(lConnection, "DROP TABLE IF EXISTS test"); } catch (ClassNotFoundException ex) { System.out.println("ClassNotFoundException: " + ex.getMessage()); } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } finally { if (lConnection != null) try { lConnection.close(); } catch (SQLException ex) { System.out.println("SqlException: " + ex.getMessage()); } } } // public static void main(String[] args) { MySqlDemo lDemo = new MySqlDemo(); lDemo.MySqlConnectionTest(); } // } // class
// .h file #pragma once #include <stdlib.h> #include <iostream> #include "mysql_connection.h" #include <cppconn/driver.h> #include <cppconn/exception.h> #include <cppconn/resultset.h> #include <cppconn/statement.h> #include <cppconn/prepared_statement.h> using namespace std; using namespace sql; class Day6 { private: void ExecuteNonQuery(Statement * const xStatement, const string &xCommand); void ExecuteNonQueryAddRow(PreparedStatement *xPreparedStatement, const string &xName, const int &xYear, const int &xMonth, const int &xDay); ResultSet *ExecuteQuery(Connection *xConnection, const string &xCommand); public: int test(void); }; // .cpp file #include "stdafx.h" #include "Day6.h" void Day6::ExecuteNonQuery(Statement * const xStatement, const string &xCommand) { SQLString lCommand(xCommand); xStatement->execute(lCommand); } // void Day6::ExecuteNonQueryAddRow(PreparedStatement *xPreparedStatement, const string &xName, const int &xYear, const int &xMonth, const int &xDay) { SQLString lName(xName); xPreparedStatement->setString(1, lName); int64_t lBirthday = xYear * 10000 + xMonth * 100 + xDay; xPreparedStatement->setInt64(2, lBirthday); xPreparedStatement->executeUpdate(); } // ResultSet *Day6::ExecuteQuery(Connection *xConnection, const string &xCommand) { SQLString lCommand(xCommand); PreparedStatement *lPreparedStatement = xConnection->prepareStatement(lCommand); ResultSet *lResultSet = lPreparedStatement->executeQuery(); delete lPreparedStatement; return lResultSet; } // int Day6::test(void) { try { // connect Driver *lDriver = get_driver_instance(); Connection *lConnection = lDriver->connect("tcp://127.0.0.1:3306", "root", "root"); // lConnection->setAutoCommit(0); // turn off auto commit lConnection->setSchema("ohta"); // replace it with your database name // create a new table string lField1 = "id INT NOT NULL AUTO_INCREMENT,"; string lField2 = "name VARCHAR(40) NOT NULL,"; string lField3 = "birthday INT,"; // YYYYMMDD string lKey = "PRIMARY KEY (id)"; string lTableDefinition = lField1 + lField2 + lField3 + lKey; string lCommand = "CREATE TABLE test(" + lTableDefinition; lCommand += ")"; Statement *lStatement = lConnection->createStatement(); ExecuteNonQuery(lStatement, "DROP TABLE IF EXISTS test"); ExecuteNonQuery(lStatement, lCommand); delete lStatement; // fill the table with values PreparedStatement *lPreparedStatement = lConnection->prepareStatement("INSERT INTO test (name, birthday) values (?, ?)"); ExecuteNonQueryAddRow(lPreparedStatement, "George Washington", 1732, 2, 22); ExecuteNonQueryAddRow(lPreparedStatement, "Abraham Lincoln", 1809, 2, 12); ExecuteNonQueryAddRow(lPreparedStatement, "Ronald Reagan", 1911, 2, 6); ExecuteNonQueryAddRow(lPreparedStatement, "Bill Clinton", 1946, 8, 19); delete lPreparedStatement; // Query ResultSet *lResultSet = ExecuteQuery(lConnection, "SELECT * FROM test;"); // print column names ResultSetMetaData *lMeta = lResultSet->getMetaData(); unsigned int lColumnCount = lMeta->getColumnCount(); for (unsigned int i = 1; i <= lColumnCount; i++) { SQLString lColumnName = lMeta->getColumnName(i); // http://bugs.mysql.com/bug.php?id=70006 SQLString lTypeName = lMeta->getColumnTypeName(i); cout << lColumnName.asStdString() << ": " << lTypeName.asStdString() << endl; } // print rows lResultSet->beforeFirst(); while (lResultSet->next()) { string lId = to_string(lResultSet->getInt("id")); SQLString h = lResultSet->getString("name"); // http://bugs.mysql.com/bug.php?id=70006 const string lName = h.asStdString(); int64_t lBirthday = lResultSet->getInt("birthday"); int lYear = (int)( lBirthday / 10000); int lMonth = (int)(lBirthday % 10000) / 100; int lDay = (int)(lBirthday % 100); cout << lId << ", " << lName << ", " << lYear << "/" << lMonth << "/" << lDay << endl; } // clean up lStatement = lConnection->createStatement(); ExecuteNonQuery(lStatement, "DROP TABLE IF EXISTS test"); delete lStatement; delete lResultSet; lConnection->close(); delete lConnection; } catch (SQLException &e) { cout << "exception" << e.getSQLState() << endl; } return EXIT_SUCCESS; }
migration C#, Java, C++ (day 5), lambda and TBB
Phew, this was a terrible day. I wanted to do far more, but I had realized that there is no proper native threading in the C++ standard libraries. Back in the 1990s threading was a minor issue. We only had one CPU and FPU. We were concentrating more on the processes and devices at that time.
I had already mentioned that I wanted to avoid Boost and Qt. The best option I found was TBB, which seems to support multicore-processors very well. I will include TBB in my migration project from now on. Today, there is only one TBB example. It took me a lot of time to get the Intel software installed on my PC.
Finally, after all these years … we can solve problems with PCs that we would not have without them 😉
TBB covers only a small fraction today. This post is mainly about lambda functions.
In C++ you can find a lot of weird stuff that has been solved in a much better way in C#. As a hardcore C# programmer C++ might feel medieval sometimes. Rome was built bit by bit. Hence its streets are chaotic. C# maybe is like Chicago. When the city was built, people had time to think about the infrastructure before they started digging arbitrary holes in the ground.
Let’s see what my thoughts will be on day 15.
Please also check my post Lambda expressions (advanced). I mentioned that the C# compiler takes care of the local variable life-cycles, when lambda expressions use local variables, which would become invalid by the time these expressions try to use them. Remember, these local variables are not created inside the lambda expressions themselves.
Today’s prerequisite for C++
#include <iostream> #include <vector> #include <ctime> #include <thread> #include "tbb/tbb.h" #include "tbb/parallel_for.h" using namespace std;
Lambda
public void DoSomething() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " "); Thread.Sleep(200); } // public void Lambdas() { // functor // There are no functors in C#. // Action or Func delegates are the closest approaches. Action lAction = DoSomething; lAction(); // lambda expression Func<int, int> func2 = (int x) => { return ++x; }; int r2 = func2(4); Console.WriteLine(r2); // 5 // lambda expression capturing a local variable // the compiler takes care of the lifetime of variable i int i = 5; Func<int, int> func3 = (int x) => { return x + i; }; int r3 = func3(4); Console.WriteLine(r3); // 9 // lambda expression capturing any local variable by reference List<int> lList = new List<int>() { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }; int lSum1 = 0; lList.ForEach((int q) => { lSum1 += q; }); Console.WriteLine(lSum1); // 101 // lambda expression with a specific return type Func<int, bool> func6 = (int x) => { x++; return true; }; bool r6 = func6(4); Console.WriteLine(r6); // True } //
class Class1 implements Runnable { @Override public void run() { try { System.out.println(Thread.currentThread().getId() + " "); Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } // class public static void main(String[] args) { Class1 c1 = new Class1(); c1.run(); // lambda expression Function<Integer, Integer> func2 = i -> { return ++i; }; Integer r2 = func2.apply(4); System.out.println(r2); // 5 // lambda expression capturing a local variable // the compiler takes care of the lifetime of variable i Integer i = 5; Function<Integer, Integer> func3 = x -> { return x + i; }; int r3 = func3.apply(4); System.out.println(r3); // 9 // lambda expression capturing any local variable by reference java.util.ArrayList<Integer> lList = new java.util.ArrayList<>(java.util.Arrays.asList(new Integer[]{1, 2, 3, 5, 7, 11, 13, 17, 19, 23})); Integer lSum1 = 0; lList.forEach((Integer q) -> { //lSum1 += q; compiler complains, because it has no write access to lSum1 }); // thus we have to use: lSum1 = lList.stream().map((q) -> q).reduce(lSum1, Integer::sum); System.out.println(lSum1); // 101 // lambda expression with a specific return type Function<Integer, Boolean> func6 = x -> { x++; return true; }; boolean r6 = func6.apply(4); System.out.println(r6); // true } //
class func1 { public: int operator ()(int x) { return ++x; } }; void Lambdas() { // functor int r1 = func1()(3); cout << r1 << endl; // 4 // lambda expression auto func2 = [](int x) { return ++x; }; int r2 = func2(4); cout << r2 << endl; // 5 // lambda expression capturing a local variable int i = 5; auto func3 = [i](int x) { return x + i; }; int r3 = func3(4); cout << r3 << endl; // 9 // lambda expression capturing any local variable auto func4 = [=](int x) { return x + i + r2 * r1; }; // 1 + 5 + (5 * 4) int r4 = func4(1); cout << r4 << endl; // 26 // lambda expression capturing a local variable by reference vector<int> v1{ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }; int lSum1 = 0; for (int q : v1) { [&lSum1](int x) {lSum1 += x; } (q); } cout << lSum1 << endl; // 101 // lambda expression capturing any local variable by reference vector<int> v2{ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }; int lSum2 = 0; int lProduct2 = 1; for (int q : v2) { [&](int x) {lSum2 += x; lProduct2 *= x; }(q); } cout << lSum2 << " " << lProduct2 << endl; // 101 223092870 // lambda expression capturing any local variable by value // The external variables can be assigned, but they are not referenced, // because the lambda expression is only working with local copies. // This can be achieved by using the keyword "mutable". // By default lambdas are not mutable. int lSum3 = 0; int lProduct3 = 1; auto func5 = [=](int x) mutable {lSum3 += x; lProduct3 *= x; return x; }; int r5 = func5(1); cout << lSum3 << " " << lProduct3 << " " << r5 << endl; // 0 1 1 // lambda expression with a specific return type auto func6 = [](int x) -> bool { x++; return true; }; bool b6 = func6(6); cout << (b6 ? "true" : "false") << endl; // true } //
TBB
public void TBB() { const int lSize = 100; Stopwatch lStopwatch = new Stopwatch(); lStopwatch.Start(); for (int i = 0; i < lSize; i++) DoSomething(); lStopwatch.Stop(); Console.WriteLine(); Console.WriteLine("time for serial loop was " + lStopwatch.ElapsedMilliseconds / 1000.0 + " seconds"); lStopwatch.Reset(); lStopwatch.Start(); Parallel.For(0, lSize, i => DoSomething()); lStopwatch.Stop(); Console.WriteLine(); Console.WriteLine("time for parallel loop was " + lStopwatch.ElapsedMilliseconds/1000.0 + " seconds"); } //
example output:
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
time for serial loop was 20.001 seconds
9 10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9
10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9 10
11 13 14 12 15 17 16 9 10 11 13 14 12 15 16 9 10 11 13 14 12 15 16 9 10 11 13 1
4 12 15 9 10 11 13 14 12 15 9 10 11 13 14 12 15
time for parallel loop was 2.408 seconds
public final void TBB() { Class1 lDoSomething = new Class1(); final int lSize = 100; long lStopwatch = System.currentTimeMillis(); for (int i = 0; i < lSize; i++) { lDoSomething.run(); } long lDiff = System.currentTimeMillis() - lStopwatch; System.out.println(); System.out.println("time for serial loop was " + lDiff / 1000 + " seconds"); lStopwatch = System.currentTimeMillis(); ArrayList<Class1> lList = new ArrayList<>(); for (int i = 0; i < lSize; i++) lList.add(lDoSomething); lList.parallelStream().forEach(x -> x.run()); lDiff = System.currentTimeMillis() - lStopwatch; System.out.println(); System.out.println("time for parallel loop was " + lDiff / 1000.0 + " seconds"); } //
example output:
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
time for serial loop was 20 seconds
12 13 14 1 16 11 15 17 14 13 12 1 16 11 15 17 13 12 14 1 16 17 11 15 13 14 12 16 1 11 15 17 13 14 12 16 1 11 15 17 12 13 14 1 16 11 17 15 14 12 13 16 1 15 17 11 12 13 14 16 1 17 11 15 14 13 12 1 16 11 15 17 13 14 12 1 16 15 17 11 14 12 13 16 1 11 17 15 14 12 13 16 1 17 11 15 12 13 1 15
time for parallel loop was 2.841 seconds
void DoSomething() { cout << this_thread::get_id() << " "; this_thread::sleep_for(chrono::milliseconds(200)); } // void TBBs() { const int lSize = 100; time_t lTimer; time(&lTimer); for (int i = 0; i < lSize; i++) DoSomething(); double lSecondsA = difftime(time(nullptr), lTimer); cout << endl << "time for serial loop was " << lSecondsA << " seconds" << endl; time(&lTimer); tbb::parallel_for(0, lSize, 1, [&](int i) { DoSomething(); }); double lSecondsB = difftime(time(nullptr), lTimer); cout << endl << "time for parallel loop was " << lSecondsB << " seconds" << endl; } //
example output:
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 46
16 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 46
16 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 46
16 4616 4616 4616 4616 4616 4616
time for serial loop was 20 seconds
4616 4068 4916 3788 2880 4168 4752 6796 4616 4916 4068 2880 3788 41
68 6796 4752 4616 4068 4916 2880 3788 4168 6796 4752 4616 4916 4068
2880 3788 4168 6796 4752 4616 4916 4068 3788 2880 4168 6796 4752
4616 4068 4916 2880 3788 4168 6796 4752 4616 4068 4916 3788 2880 41
68 6796 4752 4616 4068 4916 2880 3788 6796 4168 4752 4616 4068 4916
2880 3788 6796 4168 4752 4616 4068 4916 2880 3788 6796 4168 4752
4616 4916 4068 3788 2880 6796 4168 4752 4616 4068 4916 3788 2880 67
96 4168 4752 4616 4916 4068 3788
time for parallel loop was 2 seconds
migration C#, Java, C++ (day 4)
Today we translate some basics of threading and locking. For the ones, who follow the C# hardcore posts, I will cover the C# side in more detail on Tuesday.
Further down I also cover operator overloading today.
Some background info:
Deadlocks
WaitOne
There are no direct equivalents in C++ for the C# lock keyword, the Monitor class and Semaphores unless you are using the .Net Framework on the C++ side. This is clearly not what you want to do. The speed advantage of C++ would end in smoke.
Mutexes do exist for both C# and C++. You have to get used to using Mutexes in C++. They are hardly used in C#, but found often in C++.
The C++ mutex is not the same as the C# Mutex. This is as confusing as using a C# List and think it would be similar to a C++ list.
The C++ mutex is (depite its name) much closer to the C# Monitor class. It definitely is not as public as the C# Mutex, which is known to each process across the entire computer system.
Java looks quite retarded today. We already know that Java does not support pointers. Today it becomes even worse. There is no C# type mutex and no operator overloading.
Lock, Monitor, Semaphore
public void DoSomething() { Console.WriteLine("Good night my dear thread " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); } // object lAnyObject2 = new object(); public void LockUsage() { lock (lAnyObject2) { DoSomething(); } } // object lAnyObject1 = new object(); public void MonitorUsage() { bool lLocked = false; try { Monitor.Enter(lAnyObject1, ref lLocked); // using System.Threading; DoSomething(); } finally { if (lLocked) Monitor.Exit(lAnyObject1); } } // // Limits the number of threads that can access a resource or pool of resources concurrently. private static Semaphore _Semaphore = new Semaphore(3, 3); // three objects allowed to access concurrently public void SemaphoreUsage() { bool lLocked = false; try { lLocked = _Semaphore.WaitOne(); DoSomething(); } finally { if (lLocked) _Semaphore.Release(); } } //
public final void DoSomething() { System.out.println("Good night my dear thread " + Thread.currentThread().getId()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } private Object lAnyObject2 = new Object(); public final void LockUsage() { synchronized (lAnyObject2) { DoSomething(); } } // Limits the number of threads that can access a resource or pool of resources concurrently. private static java.util.concurrent.Semaphore _Semaphore = new java.util.concurrent.Semaphore(3); // three objects allowed to access concurrently public final void SemaphoreUsage() { try { _Semaphore.acquire(); DoSomething(); } catch (InterruptedException e) { e.printStackTrace(); } finally { _Semaphore.release(); } }
Mutex
public void DoSomething() { Console.WriteLine("Good night my dear thread " + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); } // private const string cMutexName = "MyMutex"; private static Mutex _Mutex = new Mutex(false, cMutexName); // known by every process public void MutexUsage() { bool lLocked = false; try { // optional complexity: access rights //bool lNewCreation; //MutexSecurity lSecurity = new MutexSecurity(); //SecurityIdentifier lId = new SecurityIdentifier(WellKnownSidType.WorldSid, null); //MutexRights lRights = MutexRights.Synchronize | MutexRights.Modify; //lSecurity.AddAccessRule(new MutexAccessRule(lId, lRights, AccessControlType.Allow)); //_Mutex = new Mutex(false, cMutexName, out lNewCreation, lSecurity); //MutexSecurity lReverse = _Mutex.GetAccessControl(); lLocked = _Mutex.WaitOne(2000); // You can the option to set a time limit. Here 2000 milliseconds. if (!lLocked) { Console.WriteLine("Try again later. Mutex is used by another process or thread."); } DoSomething(); } finally { if (lLocked == true) _Mutex.ReleaseMutex(); } } //
// no mutexes available // use the synchronized block
void DoSomething() { cout << "Good night my dear thread " << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(2000)); } mutex _Mutex; void MutexUsage(){ _Mutex.lock(); try { DoSomething(); } catch (string e){ _Mutex.unlock(); throw e; } _Mutex.unlock(); } mutex _Mutex2; void MultipleMutexUsage(){ int x = try_lock(_Mutex, _Mutex2); if (x == -1) { DoSomething(); _Mutex.unlock(); _Mutex2.unlock(); } else cout << "failed to obtain lock for " << (x ? "_Mutex" : "_Mutex2") << endl; } void MutexUsage_For_Forgetful_People(){ lock_guard<mutex> guard(mutex); DoSomething(); } // auto unlocks here
operator overloading
public class myClass { // this class is not thread safe int[] Values = { 1, 2, 3 }; public static myClass operator +(myClass a, myClass b) { int n = a.Values.Length; myClass lNewClass = new myClass(); for (int i = 0; i < n; i++) lNewClass.Values[i] = a.Values[i] + b.Values[i]; return lNewClass; } // public static double operator *(myClass a, myClass b) { int n = a.Values.Length; int lSum = 0; for (int i = 0; i < n; i++) lSum += a.Values[i] * b.Values[i]; return lSum; } // public static string operator +(string a, myClass b) { //return ">> " + a + b + "<<"; // WRONG! causes recursion return ">> " + a + b.ToString() + "<<"; } // // I will explanation this in my post on Tuesday 4 February 2014 // uncomment this and play with it (=>hardcore C#) //public static string operator +(myClass a, string b) { // //return ">> " + a + b + "<<"; // WRONG! causes recursion // return ">> " + a + b.ToString() + "<<"; //} // // becomes even more hardcore when you have two conflicting overloads public override string ToString() { return "Values: " + Values[0] + " " + Values[1] + " " + Values[2] + " "; } // } // class public static void test() { myClass a = new myClass(); myClass b = new myClass(); myClass c = a + b; double d = a * b; Console.WriteLine("(Sum) " + c); // ">> (Sum) Values: 2 4 6 <<" Console.WriteLine("(Sum) " + c.ToString()); // "(Sum) Values: 2 4 6" Console.WriteLine(c + " (Sum)"); // "Values: 2 4 6 (Sum)" Console.WriteLine(d); // 14 Console.ReadLine(); } //
// there is no operator overloading available in Java
#include <string> #include <iostream> #include <memory> using namespace std; namespace OperatorOverloading { class myClass { private: int *Values = new int[] { 1, 2, 3 }; public: myClass &operator + (const myClass &b); //const string operator + (const myClass &b); // not possible to distinguish by return type only const string myClass::operator + (int &i); myClass &operator + (const int &i); // this is possible 🙂 double operator *(const myClass &b); const string ToString(); }; // class myClass &myClass::operator + (const myClass &b) { myClass *lNewClass = new myClass(); for (int i = 0; i < 3; i++) lNewClass->Values[i] = Values[i] + b.Values[i]; return *lNewClass; } myClass &myClass::operator + (const int &i) { myClass *lNewClass = new myClass(); for (int i = 0; i < 3; i++) lNewClass->Values[i] = 3 + Values[i]; return *lNewClass; } const string myClass::operator + (int &i) { return ">> " + ToString() + " -- " + to_string(i) + "<<"; } // const string myClass::operator + (const myClass &b) {} not possible double myClass::operator *(const myClass &b) { int lSum = 0; for (int i = 0; i < 3; i++) lSum += (this->Values[i]) * (b.Values[i]); return lSum; } const string myClass::ToString() { string s1 = to_string(Values[0]); string s2 = to_string(Values[1]); string s3 = to_string(Values[2]); return "Values: " + s1 + " " + s2 + " " + s3; } } // namespace int main() { using namespace OperatorOverloading; myClass a, b; myClass c = a + b; cout << c.ToString() << endl; // Values: 2 4 6 double d = a * b; cout << d << endl; // 14 myClass e = a + 3; cout << c.ToString() << endl; // Values: 5 7 9 cin.get(); return 0; }
migration C#, Java, C++ (day 3)
Containers in C++ are like C# collections.
The sequential containers are vector, deque and list. The latter is by far the slowest one. List comes close to LinkedList in C#. Vector and deque are like Lists in C#.
Available Methods:
vector | deque | list | |
similar in C# | List | List | LinkedList |
front(), back() | yes | yes | yes |
push_back(), pop_back() | yes | yes | yes |
push_front(), pop_front() | no | yes | yes |
at(), operator [] | yes | yes | no |
#include <iostream> #include <string> #include <list> #include <algorithm> #include <vector> #include <deque> #include <map> #include <unordered_map> #include <set> #include <unordered_set> #include <memory>
LinkedList
LinkedList<string> lLinkedList = new LinkedList<string>(); LinkedListNode<string> lLast = lLinkedList.AddLast("Last"); LinkedListNode<string> lFirst = lLinkedList.AddFirst("First"); LinkedListNode<string> lMid = lLinkedList.AddAfter(lFirst, "Mid"); bool b1 = lLinkedList.Contains("Last"); LinkedListNode<string> lFind = lLinkedList.Find("Last"); lLinkedList.Remove("First"); lLinkedList.RemoveFirst(); lLinkedList.Remove(lLast); lLinkedList.Clear();
java.util.LinkedList<String> lLinkedList = new java.util.LinkedList<String>(); lLinkedList.addLast("Last"); lLinkedList.addFirst("First"); lLinkedList.add(1, "Mid"); boolean b1 = lLinkedList.contains("Last"); int lFind = lLinkedList.indexOf("Last"); //lLinkedList.remove(lFind); lLinkedList.remove("First"); lLinkedList.removeFirst(); lLinkedList.removeLast(); lLinkedList.clear();
list<string> lLinkedList = list<string>(); lLinkedList.push_back("Last"); lLinkedList.push_front("First"); list<string>::iterator lIterator = lLinkedList.begin(); lIterator++; lLinkedList.insert(lIterator, "Mid1"); lLinkedList.insert(lIterator, "Mid2"); cout << "List: " << endl; for (lIterator = lLinkedList.begin(); lIterator != lLinkedList.end(); lIterator++) cout << *lIterator << " "; //List: First Mid1 Mid2 Last lLinkedList.remove("First"); lLinkedList.pop_front(); lLinkedList.remove("Mid2"); cout << endl << endl << "List: " << endl; for (lIterator = lLinkedList.begin(); lIterator != lLinkedList.end(); lIterator++) cout << *lIterator << " "; //List: Last lLinkedList.clear();
List
List<string> lList = new List<string>(); lList.Add("First"); lList.AddRange(new string[] { "Mid1", "Last" }); bool b2 = lList.Contains("Mid1"); lList.Insert(2, "Mid2"); // First, Mid1, Mid2, Last lList.Reverse(); // Last, Mid2, Mid1, First lList.Sort(); // First, Last, Mid1, Mid2 string[] lStringArray = lList.ToArray(); lList.Clear();
java.util.ArrayList<String> lList = new java.util.ArrayList<String>(); lList.add("First"); lList.addAll(Arrays.asList("Mid1", "Last")); boolean b2 = lList.contains("Mid1"); // true lList.add(2, "Mid2"); // First, Mid1, Mid2, Last java.util.Collections.reverse(lList); // Last, Mid2, Mid1, First java.util.Collections.sort(lList); // First, Last, Mid1, Mid2 String[] lStringArray = lList.toArray(new String[0]); lList.clear();
inline bool MyDataSortPredicate(const string &s1, const string &s2) { return s1 < s2; } vector<string> lList = vector<string>(); lList.push_back("First"); string lArray[] {lList.front(), "Mid", "Last"}; lList.assign(lArray, lArray + 3); lList.push_back("VeryLast"); // First, Mid, Last, VeryLast reverse(lList.begin(), lList.end()); // VeryLast, Last, Mid, First sort(lList.begin(), lList.end(), MyDataSortPredicate); // First, Last, Mid, VeryLast string *lStringArray = lList.data(); lStringArray++; string lResult = lStringArray[1]; // Mid lList.clear(); deque<string> lList2 = deque<string>(); lList2.push_back("First"); string lArray2[] {lList2.front(), "Mid", "Last"}; lList2.assign(lArray2, lArray2 + 3); auto lIterator2 = lList2.end(); lIterator2--; lList2.emplace(lIterator2, "Mid2"); lList2.push_back("VeryLast"); // First, Mid, Mid2, Last, VeryLast reverse(lList2.begin(), lList2.end()); // VeryLast, Last, Mid2, Mid, First sort(lList2.begin(), lList2.end(), MyDataSortPredicate); // First, Last, Mid, Mid2, VeryLast lList2.clear();
Collections comparison
// key value pairs (no duplicate keys) Dictionary<string, string> lDictionary = new Dictionary<string, string>(); // uses a hashmap SortedDictionary<string, string> lSortedDictionary = new SortedDictionary<string, string>(); // uses a binary tree SortedList<string, string> lSortedList = new SortedList<string, string>(); // uses arrays // lists List<string> lList3 = new List<string>(); // duplicate entries allowed HashSet<string> lHashSet = new HashSet<string>(); // a List with no duplicates entries; behaves like a Dictionary with key==value SortedSet<string> lSortedSet = new SortedSet<string>(); // a sorted list with no duplicates; behaves like a SortedDictionary with key==value
// key value pairs (no duplicate keys) java.util.HashMap<String, String> lDictionary = new java.util.HashMap<String, String>(); java.util.TreeMap<String, String> lSortedDictionary = new java.util.TreeMap<String, String>(); // There is no such as a SortedList in Java // lists java.util.ArrayList<String> lList3 = new java.util.ArrayList<String>(); java.util.HashSet<String> lHashSet = new java.util.HashSet<String>(); java.util.TreeSet<String> lSortedSet = new java.util.TreeSet<String>();
// key value pairs (no duplicate keys) unordered_map<string, string> lDictionary = unordered_map<string, string>(); // uses a hashmap map<string, string> lSortedDictionary = map<string, string>(); // uses a binary tree // lSortedList = no equivalent // lists vector<string> lList2 = vector<string>(); // duplicate entries allowed unordered_set<string> lHashSet = unordered_set<string>(); // a List with no duplicates entries; behaves like a Dictionary with key==value set<string> lSortedSet = set<string>(); // a sorted list with no duplicates; behaves like a SortedDictionary with key==value
Threading
void f1() { Thread.Sleep(500); Console.WriteLine("f1 says: Hello world!"); } void f2(object o) { Thread.Sleep(1000); string s; if (o is string) s = o as string; else { // for sure using a "volatile" variable in a class as storage would be a better approach string[] h = (string[])o; s = h[0]; h[0] = "new Message"; } Console.WriteLine("f2 says: " + s); } Console.WriteLine("The parent thread id is: " + Thread.CurrentThread.ManagedThreadId); Thread t1 = new Thread(f1); // "f1 says: Hello world!" t1.IsBackground = true; // not the same as daemon! It is a background thread that is dependent from the main thread. t1.Start(); t1.Join(); string lByVal = "Agent Smith is everywhere :("; Thread t2 = new Thread(f2); // "f2 says: Agent Smith is everywhere :(" t2.Start(lByVal); string[] lByRef = { "Agent Smith is everywhere :(" }; Thread t3 = new Thread(f2); // "f2 says: Agent Smith is everywhere :(" t3.Start(lByRef); t3.Join(); Console.WriteLine("same text? " + lByRef[0]); // "same text? new Message"
class Class1 implements Runnable { @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("c1 says: Hello world!"); } } // class class Class2 implements Runnable { private Object _Parameter; public Class2(Object xParameter) { _Parameter = xParameter; } // constructor public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String s; if (_Parameter instanceof String) s = (String)_Parameter; else { String[] h = (String[])_Parameter; s = h[0]; h[0] = "new Message"; } System.out.println("c2 says: " + s); } // } // class System.out.println("The parent thread id is: " + Thread.currentThread().getId()); Class1 lInstance1 = new Class1(); Thread t1 = new Thread(lInstance1); // "c1 says: Hello world!" t1.setDaemon(true); t1.start(); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } String lByVal = "Agent Smith is everywhere :("; Class2 lInstance2 = new Class2(lByVal); Thread t2 = new Thread(lInstance2); // "c2 says: Agent Smith is everywhere :(" t2.start(); String[] lByRef = {"Agent Smith is everywhere :("}; Class2 lInstance3 = new Class2(lByRef); Thread t3 = new Thread(lInstance3); // "c2 says: Agent Smith is everywhere :(" t3.start(); try { t3.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("same text? " + lByRef[0]); // "same text? new Message"
void f1() { this_thread::sleep_for(chrono::milliseconds(500)); cout << "f1 says: Hello world!" << endl; } void f2(string xMessage) { this_thread::sleep_for(chrono::milliseconds(1000)); cout << "f2 says: " << xMessage << endl; } class f3 { public: void operator()(string &xMessage) { this_thread::sleep_for(chrono::milliseconds(1500)); cout << "functor f3 says: " << xMessage << endl; xMessage = "new Message"; } }; cout << "The parent thread id is: " << this_thread::get_id() << endl; thread t1(f1); // "f1 says: Hello world!" t1.detach(); // t1 becomes a daemon process (background + no terminal) if (t1.joinable()) t1.join(); // joinable() returns false thread t2(f2, "Mr Anderson is Neo."); // "f2 says: Mr Anderson is Neo." string lByValue = "Agent Smith is everywhere :("; thread t3((f3()), lByValue); // functor f3 says: Agent Smith is everywhere :(" t2.join(); t3.join(); cout << "same text? " << lByValue << endl; // "same text? Agent Smith is everywhere :(" string lByRef = lByValue; thread t4((f3()), ref(lByRef)); // "functor f3 says: Agent Smith is everywhere :(" // so that the call by reference can assign a new value // play with this value t4.join(); cout << "same text? " << lByRef << endl; // "same text? new Message"
migration C#, Java, C++ (day 2)
Day 2. I feel some heat already 😉
managed code
public class Dog { public string Name { get; set; } public Dog() { } public Dog(string xName) { Name = xName; } } // -------------------------------------------------------------- { Dog lDog1 = new Dog(); Dog lDog2 = new Dog(); lDog1 = null; // allows the garbage collector to collect lDog1 } // leaving the scope allows lDog2 to be collected
public class Dog { public Dog() {} public Dog(String xName) { setName(xName); } private String _Name; public final String getName() { return _Name; } public final void setName(String value) { _Name = value; } } // class // -------------------------------------------------------------- { Dog lDog1 = new Dog(); Dog lDog2 = new Dog(); lDog1 = null; // allows the garbage collector to collect lDog1 } // leaving the scope allows lDog2 to be collected
// .h file #ifndef DOG_H #define DOG_H #include <string> #include <memory> using namespace std; class Dog : public enable_shared_from_this<Dog> { private: string pName; public: const string &getName() const; void setName(const string &xName); Dog(); Dog(const string &xName); }; #endif // DOG_H // .cpp file #include "Dog.h" using namespace std; Dog::Dog(){} Dog::Dog(const string &xName) { setName(xName); } const string &Dog::getName() const { return pName;} void Dog::setName(const string &value) { pName = value; } // -------------------------------------------------------------- shared_ptr<Dog> lDog1 (new Dog("Snoopy")); cout<<lDog1.use_count() << endl; // 1 shared_ptr<Dog> lDog2 = make_shared<Dog>("Lassie"); cout<<lDog2.use_count() << endl; // 1 shared_ptr<Dog> lDog3 = lDog2; cout<<lDog1.use_count() << endl; // 1 cout<<lDog2.use_count() << endl; // 2 cout<<lDog3.use_count() << endl; // 2
Dictionary
Dictionary<string, Double> lDictionary = new Dictionary<string, double>(); lDictionary.Add("-10-", 10.0); lDictionary.Add("-20-", 20.0); lDictionary.Add("-30-", 30.0); lDictionary.Add("-40-", 40.0); lDictionary.Remove("-30-"); foreach (var lPair in lDictionary) Console.WriteLine(lPair.Key + " equals " + lPair.Value); foreach (var lValue in lDictionary.Values) Console.WriteLine(lValue); Double d; if (lDictionary.TryGetValue("-10-", out d)) Console.WriteLine("found -10- which is: " + d);
java.util.HashMap<String, Double> lDictionary = new java.util.HashMap<String, Double>(); lDictionary.put("-10-", 10.0); lDictionary.put("-20-", 20.0); lDictionary.put("-30-", 30.0); lDictionary.put("-40-", 40.0); lDictionary.remove("-30-"); // C# var: There is no such as implicit typing in Java for (Entry<String, Double> lPair : lDictionary.entrySet()) System.out.println(lPair.getKey() + " equals " + lPair.getValue()); for (Double lValue : lDictionary.values()) System.out.println(lValue); double d = 0; if (lDictionary.containsKey("-10-") ? (d = lDictionary.get("-10-")) == d : false) System.out.println("found -10- which is: " + d);
unordered_map<string, double> lMap = unordered_map<string, double>(); lMap.insert(make_pair("-10-", 10.0)); lMap.insert(make_pair("-20-", 20.0)); lMap.insert(make_pair("-30-", 30.0)); lMap.insert(make_pair("-40-", 40.0)); lMap["-40-"] = 41.0; lMap.erase("-30-"); for (auto &lPair : lMap) { cout << lPair.first << " equals " << lPair.second << endl; // 10 20 41 } double d1 = lMap["-10-"]; cout << "found -10- which is: " << d1 << endl; // 10.0 double d2 = lMap["-11-"]; // returns the default value cout << "found -11- which is: " << d2 << endl; // 0.0 unordered_map<string, string> lMap2 = unordered_map<string, string>(); lMap2["hello"] = "hulla"; string s = lMap2["hola"]; if (s == "") s = "EMPTY"; cout << "lMap2[\"hola\"] == " << s << endl; // s == "EMPTY"
Queue
Queue<Dog> lDogs = new Queue<Dog>(); lDogs.Enqueue(new Dog("Boomer")); lDogs.Enqueue(new Dog("Spike")); lDogs.Enqueue(new Dog("Lassie")); Console.WriteLine(lDogs.Peek().Name); // Boomer Dog lDog = lDogs.Dequeue(); Console.WriteLine(lDogs.Peek().Name); // Spike
// Queue is not a class, just an interface java.util.Queue<Dog> lDogs = new java.util.LinkedList<Dog>(); lDogs.offer(new Dog("Boomer")); lDogs.offer(new Dog("Spike")); lDogs.offer(new Dog("Lassie")); System.out.println(lDogs.peek().getName()); // Boomer Dog lDog = lDogs.poll(); System.out.println(lDogs.peek().getName()); // Spike
#include <queue> queue<Dog*> lDogs = queue<Dog*>(); lDogs.push(new Dog("Boomer")); lDogs.push(new Dog("Spike")); lDogs.push(new Dog("Lassie")); cout << lDogs.front()->getName() << endl; // Boomer cout << lDogs.back()->getName() << endl; // Lassie lDogs.pop(); // takes Boomer from the queue cout << lDogs.front()->getName() << endl; // Spike cout << lDogs.back()->getName() << endl; // Lassie
Stack
Stack<Dog> lDogs = new Stack<Dog>(); lDogs.Push(new Dog("Boomer")); lDogs.Push(new Dog("Spike")); lDogs.Push(new Dog("Lassie")); Console.WriteLine(lDogs.Peek().Name); // Lassie Dog lDogLassie = lDogs.Pop(); Console.WriteLine(lDogs.Peek().Name); // Spike
java.util.Stack<Dog> lDogs = new java.util.Stack<Dog>(); lDogs.push(new Dog("Boomer")); lDogs.push(new Dog("Spike")); lDogs.push(new Dog("Lassie")); System.out.println(lDogs.peek().getName()); // Lassie Dog lDogLassie = lDogs.pop(); System.out.println(lDogs.peek().getName()); // Spike
stack<Dog*> lDogs = stack<Dog*>(); lDogs.push(new Dog("Boomer")); lDogs.push(new Dog("Spike")); lDogs.push(new Dog("Lassie")); cout << lDogs.top()->getName() << endl; // Lassie lDogs.pop(); // takes Lassie from the stack cout << lDogs.top()->getName() << endl; // Spike
Generics, Templates
T larger<T>(T obj1, T obj2 ) { if ((dynamic)obj1 > (dynamic)obj2) return obj1; return obj2; } // int i = larger<int>(6, 4); // i == 6 bool same<T>(T obj1, T obj2 ) where T : class { return (obj1 == obj2); } // bool b1 = same<string>("ABC", "abc"); // False bool b2 = same<string>("abc", "abc"); // True public class MyClass<T, U> where T : struct where U : class { public T MyFunc(T a, U b, T c) { return c; } } MyClass<int, string> lClass = new MyClass<int, string>(); int i = lClass.MyFunc(0, "hello", 1); // i == 1
<T extends Comparable<T>> T larger(T obj1, T obj2) { if (obj1.compareTo(obj2) > 0) return obj1; return obj2; } int i = this.<Integer>larger(6, 4); // i == 6 <T extends Comparable<T>> boolean same(T obj1, T obj2) { return (obj1 == obj2); } boolean b1 = this.<String>same("ABC", "abc"); // False boolean b2 = this.<String>same("abc", "abc"); // True class MyClass<T, U> { public final T MyFunc(T a, U b, T c) { return c; } } MyClass<Integer, String> lClass = new MyClass<Integer, String>(); int i = lClass.MyFunc(0, "hello", 1); // i == 1
template <typename T> T larger(T obj1, T obj2){ if(obj1 > obj2) return obj1; return obj2; } // int i = larger(6, 4); // i == 6 template <typename T> bool same(T obj1, T obj2){ if(obj1 == obj2) return true; return false; } // bool b1 = same("ABC", "abc"); // false bool b2 = same("abc", "abc"); // true bool b3 = same("abd", "abc"); // false template<typename T, typename U> class MyClass { U MyData; public: T MyFunc(T a, U b, T c) { return c; } void MyFunc(int a, U b) { } }; MyClass<int, string> lClass; int i = lClass.MyFunc(0, "hello", 1); // 1
simple Thread
No worries, there will be follow-ups.
private void MyThread(object x) { Console.WriteLine(x as string); } Thread lThread = new Thread(MyThread); lThread.IsBackground = true; lThread.Start("mySupaDupaaParameter");
public class MyThreadClass extends Thread { private String _Parameter; public MyThreadClass(String xParameter) { _Parameter = xParameter; } // constructor @Override public void run() { System.out.println(_Parameter); } // } // class MyThreadClass lInstance = new MyThreadClass("mySupaDupaaParameter"); Thread lThread = new Thread(lInstance); lThread.setDaemon(true); lThread.start();
#include <thread> void MyThread(string xParameter) { cout << xParameter; } thread lThread(MyThread, "mySupaDupaaParameter");
migration C#, Java, C++ (day 1)
This is day 1 of this series. I am covering some tricky basics between the two languages before we get more complicated. All should be self-explanatory. There is no clear structure yet. I just picked some examples that are worth mentioning.
There is a lot of code that cannot be written in C# or C++. Sometimes the differences are quite big. I am still trying to find a solution to show such properly in a comparison without just documenting each language by itself.
Arrays
// single-dimensional array int[] a1 = new int[5]; Console.WriteLine(a1.Length); // 5 // rectangular array int[,] a2 = new int[3, 4]; Console.WriteLine(a2.Length); // 12 Console.WriteLine(a2.GetUpperBound(0)); // 2 Console.WriteLine(a2.GetUpperBound(1)); // 3 // jagged arrays int[][] a3 = new int[5][]; for (int i = 0, n = a3.Length; i < n; i++) a3[i] = new int[i + 3]; Console.WriteLine(a3.Length); // 5 Console.WriteLine(a3[3].Length); // 6 Console.WriteLine(a3.GetUpperBound(0)); // 4
// single-dimensional array int[] a1 = new int[5]; System.out.println(a1.length); // 5 // rectangular array int[][] a2 = new int[3][4]; System.out.println(a2.length); // 3 System.out.println(a2[0].length); // 4 // jagged arrays int[][] a3 = new int[5][]; for (int i = 0, n = a3.length; i < n; i++) a3[i] = new int[i + 3]; System.out.println(a3.length); // 5 System.out.println(a3[3].length); // 6
// single-dimensional array int* a0 = new int[5]; // Size cannot be determined. Use std::vector. int a1[5]; cout << sizeof(a1) << endl; // 20 bytes cout << sizeof(a1)/sizeof(a1[0]) << endl; // 5 elements // rectangular array unsigned int a2[3][4]; cout << "int[3][4]: " << sizeof(a2) << endl; // 48 cout << " int[4]: " << sizeof(*a2) << endl; // 16 cout << " int: " << sizeof(**a2) << endl; // 4 cout << " a2: " << a2 << endl; // 0x28fed0 cout << "(*a2) + 1: " << (*a2) + 1 << endl; // 0x28fed4 (4 higher) cout << " a2 + 1: " << a2 + 1 << endl; // 0x28fee0 (16 higher) // jagged arrays int** a3 = new int*[5]; for(size_t i = 0; i < 5; ++i) a3[i] = new int[i + 3]; for(size_t i = 0; i < 5; ++i) delete[] a3[i]; delete[] a3;
Pointers
// call by reference vs. call by value void CallByValue(int x) { x = 6; } void CallByRefernce1(ref int x) { x = 1; } void CallByRefernce2(out int x) { x = 2; } public int func(int x) { return x; } int h = 3; CallByValue(h); Console.WriteLine(h); // 3 CallByRefernce2(out h); Console.WriteLine(h); // 2 CallByRefernce1(ref h); Console.WriteLine(h); // 1 // this is a simple delegate (not a pointer) Func<int, int> f = func; Console.WriteLine(f(5)); // 5
public final class delegateWrapper<T> { public T invoke(T t) { return t; } } // class public final class boxingWrapper<T> { public T value; public boxingWrapper(T xValue) { value = xValue; } } // class private void CallByValue(int x) { x = 6; } private void CallByRefernce(boxingWrapper<Integer> x) { x.value = 1; } int h = 3; boxingWrapper<Integer> lWrapper = new boxingWrapper<Integer>(h); CallByValue(h); // 3 System.out.println(h); CallByRefernce(lWrapper); h = lWrapper.value; // 1 System.out.println(h); // there are no pointers or delegates in Java delegateWrapper<Integer> lDelegate = new delegateWrapper<Integer>(); h = lDelegate.invoke(5); System.out.println(h); // 5
// call by reference vs. call by value void CallByValue(int x) { x = 6; } void CallByRefernce(int &x) { x = 1; } int func(int x) { return x; } int h = 3; CallByValue(h); cout<<h<<endl; // 3 CallByRefernce(h); cout<<h<<endl; // 1 // pointer to a method int (*f)(int) = 0; f = &func; cout<< f(5) << " " << (*f)(5) << endl; // 5 5 // pointer to integers (not available in C#) int *p1; // pointer to an integer int p2[3]; // an array of 3 integers int *p3[3]; // an array of 3 pointers to integers int (*p4)[3]; // a pointer to an array of 3 integers int *(&p5)[3] = p3; // a reference to an array of 3 pointers to integers int *(*p6[3])[4]; // an array of 3 pointers to an array of 4 pointers to integers int **(*p7[2])[3]; // an array of 2 pointers to an array of 3 pointers to pointers to integers
Strings
string s = "Hello World!"; // Unicode (24 bytes) Console.WriteLine(s.Length); // 12 if (s.CompareTo("abc") > 0) Console.WriteLine("larger"); for (int i = 0, n = s.Length; i < n; i++) Console.Write(s[i] + " "); Console.WriteLine(); foreach (char lChar in s) Console.Write(lChar + " "); Console.WriteLine(); int lPosition = s.IndexOf("ll", 0); s = s.Remove(lPosition, 2) + " / " + s.Substring(lPosition, 2); Console.WriteLine(s);
String s = "Hello World!"; // Unicode (24 bytes) System.out.println(s.length()); // 12 if (s.compareTo("abc") > 0) System.out.println("larger"); for (int i = 0, n = s.length(); i < n; i++) System.out.print(s.charAt(i) + " "); System.out.println(); char[] lChars = s.toCharArray(); for (char lChar : lChars) System.out.print(lChar + " "); System.out.println(); int lPosition = s.indexOf("ll", 0); // there is no remove, so we need to simulate it String h = s.substring(0, lPosition) + s.substring(lPosition + 2); s = h + " / " + s.substring(lPosition, lPosition + 2); System.out.println(s);
// strings (null terminated ASCII codes) string s = "Hello World!"; // == {’H’, ’e’, ’l’, ’l’, ’o’, ’ ’, ’W’, ’o’, ’l’, ’d’, ’!’, ’\0’} cout<< s << ", length: " << s.length() << endl; cout<< sizeof(s) << endl; // pointer size if (s < "abc") cout << "abc is larger" << endl; // operator overloaded in string.h for (int i = 0;s[i] != '\0'; i++) cout << s[i] << " "; cout << endl; for (int i = 0, n = s.length(); i < n; i++) cout << s.at(i) << " "; // includes boundary checks and exception handling cout << endl; for (char &c : s) cout << c << " "; // includes boundary checks and exception handling cout << endl; int lPosition = s.find("ll", 0); s = s.erase(lPosition, 2) + " / " + s.substr(lPosition, 2); cout << s << endl; // wstring holds unicode characters // (wchar_t is 2 bytes on Windows, 4 bytes on Linux) const wstring lWString = L"Hello wide world :)"; wcout<<lWString<<endl; // direct output string lString(lWString.begin(), lWString.end()); cout<<lString<<endl;
#define
// preprocessor #define is used to set flags #if DEBUG Console.WriteLine("DEBUG CODE"); try { throw new Exception(); } catch (Exception ex) { Console.WriteLine(ex.StackTrace); } #else Console.WriteLine("PROD CODE"); #endif
public static boolean DEBUG = true; // There are no preprocessors in Java. // To get as close as possible to the idea of preprocessors you // can use public static variables or Dependency Injection. public void test() { if (DEBUG) { System.out.println("DEBUG CODE"); try { throw new RuntimeException(); } catch (RuntimeException ex) { for (StackTraceElement x : ex.getStackTrace()) System.out.println(x.toString()); } } else System.out.println("PROD CODE"); }
// preprocessor #define #define OMG "Oh my god!\n" cout << OMG << endl; #define add(a, b, c) a + b + c + 1 cout << add(1, 2, 3) << endl; // 7 #define DEBUG #ifdef DEBUG cout << "DEBUG CODE" << endl; #else count << "PROD CODE" << endl; #endif cout << endl; cout << "This is the line " << __LINE__ << " of file " << __FILE__ << endl; cout << "Compilation date " << __DATE__ << " " << __TIME__ << endl;
struct vs. class
struct MyStruct { public double x; } // value type class MyClass { public double x; } // reference type MyStruct lStruct1 = new MyStruct(); MyStruct lStruct2 = lStruct1; // creates a copy of lStruct1 lStruct2.x = 1.0; lStruct1.x = 99.9; Console.WriteLine(lStruct2.x); // 1.0 ! MyClass lClass1 = new MyClass(); MyClass lClass2 = lClass1; // only assigns the reference lClass2.x = 1.0; lClass1.x = 99.9; Console.WriteLine(lClass2.x); // 99.9 !
// there are no user-defined value types like struct in Java public class MyClass { public double x; } private MyClass lClass1 = new MyClass(); private MyClass lClass2 = lClass1; // assigns the reference lClass2.x = 1.0; lClass1.x = 99.9; System.out.println(lClass2.x); // 99.9
// Structs in C++ are like classes. // The main difference is that the default for structs is public, whereas the default for classes is private. // Some people say that structs are for data (no methods) only. This is not true. Both can have methods.
Cast Operations
public class Dog { } public class Husky : Dog { } public class StyleGuide { } // cast operations double pi = 3.14159265; int lInt = (int)pi; double d = lInt; Console.WriteLine(d); // 3.0 Dog lDog = new Husky(); Husky lHusky = lDog as Husky; if (lHusky != null) Console.WriteLine("A husky is a dog"); StyleGuide lStyle = new StyleGuide(); //lHusky = (Husky)lStyle; // won't compile
public class Dog { } public class Husky extends Dog { } public class StyleGuide { } double pi = 3.14159265; int lInt = (int)pi; double d = lInt; System.out.println(d); // 3.0 Dog lDog = new Husky(); Husky lHusky = (Husky)lDog; if (lHusky != null) System.out.println("A husky is a dog"); StyleGuide lStyle = new StyleGuide(); // lHusky = (Husky)lStyle; // won't compile
// cast operations double pi = 3.14159265; int lInt = static_cast<int>(pi); double d = lInt; cout<<d<<endl; // 3.0 Dog *lDog = new Husky(); Husky *lHusky = dynamic_cast<Husky*>(lDog); if (lHusky != 0) cout<<"A husky is a dog"<< endl; StyleGuide *lStyle; lHusky = dynamic_cast<Husky*>(lStyle); if (lHusky == 0) cout<<"A StyleGuide is not a dog"<< endl; const char* lConst = "Hello World"; char* lVariable = const_cast<char*>(lConst); long lAddress = 5165786; lDog = reinterpret_cast<Dog*>(lAddress); if (lDog != 0) cout<<"What a funny dog"<< endl;
Collections:List
List<string> lList = new List<string>(); lList.Add("hello"); lList.Add("world"); foreach (var lItem in lList) Console.WriteLine(lItem); lList.Clear(); lList = null;
java.util.ArrayList<String> lList = new java.util.ArrayList<String>(); lList.add("hello"); lList.add("world"); for (String lItem : lList) System.out.println(lItem); lList.clear(); lList = null;
vector<wstring> lList = vector<wstring>(); lList.push_back(L"hello"); lList.push_back(L"world"); for (wstring &lItem : lList) wcout << lItem << endl; lList.clear();
Our first class
using System; namespace DemoApp.ToCpp { public class StyleGuide : IDisposable { private bool _Disposed = false; public StyleGuide() { } private double PrivateFunc(string s) { return 5.3; } public int PublicFunc(string s) { return 0; } protected string ProtectedFunc(string s) { return s; } ~StyleGuide() { Dispose(false); } // deconstructors call Finalize() of the base class public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool xDisposing) { if (_Disposed) return; if (xDisposing) { // free objects } _Disposed = true; } // } // class } // namespace
package DemoApp.ToCpp; public class StyleGuide { public StyleGuide() {} private double PrivateFunc(String s) { return 5.3; } public final int PublicFunc(String s) { return 0; } protected final String ProtectedFunc(String s) { return s; } @Override protected void finalize() throws Throwable { // // FreeResourcesHere(); // super.finalize(); } // destructor } // class
#ifndef STYLEGUIDE_H #define STYLEGUIDE_H #include <iostream> #include <string> using namespace std; class StyleGuide { public: StyleGuide(); virtual ~StyleGuide(); int PublicFunc(string* s); protected: string ProtectedFunc(string* s); private: double PrivateFunc(string* s); }; #endif // STYLEGUIDE_H
#include "StyleGuide.h" StyleGuide::StyleGuide() {} StyleGuide::~StyleGuide() {} double StyleGuide::PrivateFunc(string *s) { return 5.3; } int StyleGuide::PublicFunc(string *s) { return 0; } string StyleGuide::ProtectedFunc(string *s) { return *s; }
JSON (basics), follow-up to XML
XML is quite verbose. This leads to higher memory usage and slowness during the (de-)serialization process. There are alternatives in the market that also support the cross-platform approach. The most basic one, which is used among web APIs, is JSON. It is well-integrated into JavaScript.
The .Net framework supports JSON. Thus JSON is a good start to slowly delve into posts about networking. There are more advanced cross-platform solutions. Jackson, GSON and BSON are some of the really good ones. Besides this you can always use the good old .Net data serialization (or remoting). If you need speed rather than flexibility, then I would probably recommend to use your own binary protocol. Unfortunately custom binary protocols are error-prone, debugging can be hell and the control factor for IT support people is close to zero in case the protocol is corrupt. Does transmitting data take longer than encoding/packing data? That is the fundamental question. Protocol Buffers will most likely be the “binary number cruncher’s” best friend. It is a free (Apache License) cross plattforms product. There is also a specific version for the .Net environment. Protocol Buffers is not the fastest tool in the market, but all faster tools I know are (like kryo) for Java only. Protocol Buffers is supported by many programming languages. Its speed is pretty good.
As usual plenty of posts lie ahead of us.
Back to Json.
Here is a quick example from wiki:
This is a follow-up on XML. So we have to start with an XML example somehow 🙂
<person> <firstName>John</firstName> <lastName>Smith</lastName> <age>25</age> <address> <streetAddress>21 2nd Street</streetAddress> <city>New York</city> <state>NY</state> <postalCode>10021</postalCode> </address> <phoneNumbers> <phoneNumber type="home">212 555-1234</phoneNumber> <phoneNumber type="fax">646 555-4567</phoneNumber> </phoneNumbers> </person>
or
<person firstName="John" lastName="Smith" age="25"> <address streetAddress="21 2nd Street" city="New York" state="NY" postalCode="10021" /> <phoneNumbers> <phoneNumber type="home" number="212 555-1234"/> <phoneNumber type="fax" number="646 555-4567"/> </phoneNumbers> </person>
And JSON looks more like C#, doesn’t it?
Btw. there is a really nice JSON online viewer here.
{ "firstName": "John", "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] }
First of all we need to make JavaScriptSerializer available. Go to your Solution Explorer and right-click your project, then select “Properties”. Change your “Target Framework” on the “Application” page to “.Net Framework 4” or higher. Do not select “.Net Framework 4 Client Profile“.
Add the reference “System.Web.Extensions” to your project. The namespace “System.Web.Script.Serialization” is available now.
(In case it is still in your AssemblyInfo.cs, remove [assembly: AllowPartiallyTrustedCallersAttribute])
Let’s now convert our “Walmart.xml” example to JSON. The example shows how to serialize objetcs. Nothing was changed in the class WalmartWorld (see earlier post).
public static void SaveObjectsJSON(WalmartWorld xWalmartWorld) { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart.json"; JavaScriptSerializer lSerializer = new JavaScriptSerializer(); string lSerialized = lSerializer.Serialize(xWalmartWorld); File.WriteAllText(lFile, lSerialized); } //
Formatted example output:
{ "Food": [ { "Attribute": null, "Name": "Banana", "Price": 1.99, "Description": "Mexican delicious", "SomethingElse": null }, { "Attribute": null, "Name": "Rice", "Price": 0.79, "Description": "the best you can get", "SomethingElse": null }, { "Attribute": "What a wonderful world.", "Name": "Cornflakes", "Price": 3.85, "Description": "buy some milk", "SomethingElse": null }, { "Attribute": null, "Name": "Milk", "Price": 1.43, "Description": "from happy cows", "SomethingElse": null }, { "Attribute": null, "Name": "baked beans", "Price": 1.35, "Description": "very British", "SomethingElse": null } ], "Electronic": [ { "Attribute": null, "Name": "Kindle fire", "Price": 100, "Description": "Amazon loves you", "SomethingElse": "the perfect Xmas gift for your kids" } ], "Text": "Luis Armstrong had a fabulous voice." }
public static void LoadObjectJSON() { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart.json"; JavaScriptSerializer lSerializer = new JavaScriptSerializer(); string lSerialized = File.ReadAllText(lFile); WalmartWorld lWalmartWolrd = lSerializer.Deserialize<WalmartWorld>(lSerialized); foreach (WalmartItem lItem in lWalmartWolrd.Electronic) Console.WriteLine(lItem); foreach (WalmartItem lItem in lWalmartWolrd.Food) Console.WriteLine(lItem); Console.ReadLine(); } //
example output:
Kindle fire 100.00 Amazon loves you !!! => the perfect Xmas gift for your kids Banana 1.99 Mexican delicious Rice 0.79 the best you can get Cornflakes 3.85 buy some milk Milk 1.43 from happy cows baked beans 1.35 very British
Unfortunately we have the same mess as with XML. There are several classes dealing with JSON in the .Net framework.
There is another serializer in the System.Runtime.Serialization.Json namespace called DataContractJsonSerializer. It is intended for use with WCF client applications. It does make use of DataContractAttribute and DataMemberAttribute to identify the data to serialize. If you do not add any of these attributes, then data is ignored.
Add the “System.Runtime.Serialization” reference to your project.
Let’s extend our Walmart classes and add the required attributes. You can remove the XML attributes if you do not want to work with XML anymore. You can leave them in the code to keep the back door open for quick and easy XML conversion. The XML attributes do not affect the program, there are just some extra bytes.
BUT beware of a little typo. If you use DataContractSerializer instead of DataContractJsonSerializer, then you will accidentally create XML and not Json files. Such little problems in life can drive you nuts.
Further reading for DataContractAttribute and DataMemberAttribute.
[Serializable] [DataContract(Name="WalmartWorld")] [XmlRoot("Walmart", Namespace = "")] public class WalmartWorld { [XmlElement("food")] [DataMember] public List<WalmartItem> Food { get; set; } [XmlElement("electronic")] [DataMember] public List<WalmartItem> Electronic { get; set; } [XmlText] [DataMember] public string Text { get; set; } } // class [Serializable] [DataContract(Name = "WalmartItem")] [XmlRoot("Walmart", Namespace = "")] public class WalmartItem { [XmlAttribute("attr")] [DataMember(Name = "Attribute")] // just to demonstrate that you could define a name public string Attribute { get; set; } [XmlElement("name")] [DataMember] public string Name { get; set; } [XmlElement("price")] [DataMember] public double Price { get; set; } [XmlElement("description")] [DataMember] public string Description { get; set; } [XmlElement("somethingElse")] [DataMember] public string SomethingElse { get; set; } public override string ToString() { return Name.PadRight(12) + Price.ToString("#,##0.00").PadLeft(8) + " " + Description + (string.IsNullOrEmpty(SomethingElse) ? string.Empty : (" !!! => " + SomethingElse)); } // } // class public static void SaveObjectsJSON2(WalmartWorld xWalmartWorld) { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart2.json"; using (FileStream lFileStream = new FileStream(lFile, FileMode.OpenOrCreate)) { DataContractJsonSerializer lSerializer = new DataContractJsonSerializer(typeof(WalmartWorld)); lSerializer.WriteObject(lFileStream, xWalmartWorld); } } // public static WalmartWorld LoadObjectJSON2() { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart.json"; DataContractJsonSerializer lSerializer = new DataContractJsonSerializer(typeof(WalmartWorld)); using (FileStream lFileStream = new FileStream(lFile, FileMode.Open)) { WalmartWorld lWalmartWolrd = lSerializer.ReadObject(lFileStream) as WalmartWorld; foreach (WalmartItem lItem in lWalmartWolrd.Electronic) Console.WriteLine(lItem); foreach (WalmartItem lItem in lWalmartWolrd.Food) Console.WriteLine(lItem); return lWalmartWolrd; } } //
You could define your custom Name for each DataMember attribute. The JSON file would then address this name instead of the property name.
[DataMember(Name = "UseWhateverYouLike")] public string Attribute { get; set; }
Events (part 3, advanced)
Events: Let’s go multithreading! We want the crème de la crème. Well, multitasking is also fine.
The code does not need to run in a specific sequence. The required independence is given. Again, there are many ways to use multithreading. An easy approach is to start a task in each method that is called by the event.
C# does support BeginInvoke() for delegates. This method is not supported by the .NET Compact Framework though. We don’t care, because our hardcore programs are for serious applications, definitely not for mobile phone apps. Let’s see how good BeginInvoke() works. Maybe we don’t have to reinvent the wheel.
BeginInvoke() initiates asynchronous calls, it returns immediately and provides the IAsyncResult, which can be used to monitor the progress of the asynchronous call.
EndInvoke() retrieves the results. It blocks until the thread has completed.
You have the following options after calling BeginInvoke():
1) Call EndInvoke() to block the current thread until the call completes.
2) Obtain the WaitHandle from IAsyncResult.AsyncWaitHandle, use its WaitOne() and then call EndInvoke().
3) Poll the IAsyncResult to check the current state, after it has completed call EndInvoke().
4) Pass a callback to BeginInvoke(). The callback will use the ThreadPool to notify you. In the callback you have to call EndInvoke().
public event EventHandler OnChange; public void DoSomeWork(object sender, object e) { Thread.Sleep(2100); Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " mission accomplished!"); } // public void RunMyExample() { OnChange += new EventHandler(DoSomeWork); //OnChange += new EventHandler(DoSomeWork); //OnChange += new EventHandler(DoSomeWork); IAsyncResult lAsyncResult; Console.WriteLine("Choice 1"); lAsyncResult = OnChange.BeginInvoke(this, EventArgs.Empty, null, null); OnChange.EndInvoke(lAsyncResult); Console.WriteLine("Choice 2"); lAsyncResult = OnChange.BeginInvoke(this, EventArgs.Empty, null, null); lAsyncResult.AsyncWaitHandle.WaitOne(); Console.WriteLine("Choice 3"); lAsyncResult = OnChange.BeginInvoke(this, EventArgs.Empty, null, null); while (!lAsyncResult.IsCompleted) { Thread.Sleep(500); Console.WriteLine("work still not completed :("); } Console.WriteLine("Choice 4"); OnChange.BeginInvoke(this, EventArgs.Empty, (xAsyncResult) => { Console.WriteLine("callback running"); OnChange.EndInvoke(xAsyncResult); }, null); Console.WriteLine("press return to exit the program"); Console.ReadLine(); }//
example output:
Choice 1
Thread 6 mission accomplished!
Choice 2
Thread 6 mission accomplished!
work still not completed 😦
work still not completed 😦
work still not completed 😦
work still not completed 😦
Thread 10 mission accomplished!
work still not completed 😦
press return to exit the program
Thread 6 mission accomplished!
callback running
After having lived such a nice life, we have to face a major issue with BeginInvoke(). Uncomment “//OnChange += new EventHandler(DoSomeWork);”, don’t get annoyed now!
You will get an error message saying
“The delegate must have only one target”.
Although the delegate class can deal with multiple targets, asynchronous calls accept just one target.
So let’s try something else.
public event EventHandler OnChange; public void DoSomeWork(object sender, object e) { Thread.Sleep(2100); Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " mission accomplished!"); } // public void RunMyExample() { OnChange += new EventHandler(DoSomeWork); OnChange += new EventHandler((sender, e) => { throw new Exception("something went wrong"); }); OnChange += new EventHandler(DoSomeWork); EventHandler lOnChange = OnChange; if (lOnChange == null) return; // just to demonstrate the proper way to call events, not needed in this example foreach (EventHandler d in lOnChange.GetInvocationList()) { Task lTask = Task.Factory.StartNew(() => d(this, EventArgs.Empty)); lTask.ContinueWith((i) => { Console.WriteLine("Task canceled"); }, TaskContinuationOptions.OnlyOnCanceled); lTask.ContinueWith((i) => { Console.WriteLine("Task faulted"); }, TaskContinuationOptions.OnlyOnFaulted); lTask.ContinueWith((i) => { Console.WriteLine("Task completion"); }, TaskContinuationOptions.OnlyOnRanToCompletion); } Console.WriteLine("press return to exit the program"); Console.ReadLine(); }//
It seems that Microsoft has a serious bug here.This code does not execute properly each time. I guess it has to do with the asynchronous behaviour of StartNew(). It sometimes calls the wrong method DoSomeWork() three times and does not raise the exception.
It seems foreach overrides variable “d” before it is inserted in StartNew(). With a little tweak we can avoid this bug. We simply assign “d” to a new local variable. That way we have unique copies of “d”. Weird stuff, but that is the life of a coder sometimes.
public event EventHandler OnChange; public void DoSomeWork(object sender, object e) { Thread.Sleep(2100); Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " mission accomplished!"); } // public void RunMyExample() { OnChange += new EventHandler(DoSomeWork); OnChange += new EventHandler((sender, e) => { throw new Exception("something went wrong"); }); OnChange += new EventHandler(DoSomeWork); EventHandler lOnChange = OnChange; if (lOnChange == null) return; // just to demonstrate the proper way to call events, not needed in this example foreach (EventHandler d in lOnChange.GetInvocationList()) { EventHandler e = d; Task lTask = Task.Factory.StartNew(() => e(this, EventArgs.Empty)); lTask.ContinueWith((i) => { Console.WriteLine("Task canceled"); }, TaskContinuationOptions.OnlyOnCanceled); lTask.ContinueWith((i) => { Console.WriteLine("Task faulted"); }, TaskContinuationOptions.OnlyOnFaulted); lTask.ContinueWith((i) => { Console.WriteLine("Task completion"); }, TaskContinuationOptions.OnlyOnRanToCompletion); } Console.WriteLine("press return to exit the program"); Console.ReadLine(); }//
Example output:
press return to exit the program
Thread 11 mission accomplished!
Thread 10 mission accomplished!
Task completion
Task faulted
Task completion
This tiny tweak worked. You just have to know the compiler bugs. I hope this little notice saves you at least 3 hours of work.
You probably remember that we faced a similar issue in my post “Exiting Tasks (advanced)” https://csharphardcoreprogramming.wordpress.com/2013/12/11/exiting-tasks/ . I would suggest to only use Task.Factory.StartNew() with caution. Maybe it only happens in conjunction with lambda expressions.
The following code is also running very well. The variable “d” is not used directly in Task.Factory.StartNew().
... foreach (EventHandler d in lOnChange.GetInvocationList()) { Action a = () => d(this, EventArgs.Empty); Task lTask = Task.Factory.StartNew(a); lTask.ContinueWith((i) => { Console.WriteLine("Task canceled"); }, TaskContinuationOptions.OnlyOnCanceled); lTask.ContinueWith((i) => { Console.WriteLine("Task faulted"); }, TaskContinuationOptions.OnlyOnFaulted); lTask.ContinueWith((i) => { Console.WriteLine("Task completion"); }, TaskContinuationOptions.OnlyOnRanToCompletion); } ...
Events (part 2, advanced)
We are going to construct our custom event accessor now. It deals with additions and removals of subscriptions. Accessors do pretty much look like property definitions. But instead of set and get you have to use add and remove.
public class MyActionEvent4 { private object _Lock = new object(); // a new object simply to avoid lock conflicts private event EventHandler<MyArgs> _OnChange; private event EventHandler<MyArgs> OnChange { add { lock (_Lock) { _OnChange += value; } } remove { lock (_Lock) { _OnChange -= value; } } } // public void RaiseEvent() { lock (_Lock) { EventHandler<MyArgs> lHandler = _OnChange; if (lHandler == null) return; lHandler(this, new MyArgs(0)); } }// } // class
Now we have one big problem here. The RaiseEvent() method has to obtain a lock each time, which causes a serious impact on time sensitive programs. Luckily you do not need to care about changing subscriptions during the invocation. Delegate references are thread-safe, because they are immutable like strings. Let’s simply take the lock out of the RaiseEvent() method, et voilà !
public class MyActionEvent5 { private object _Lock = new object(); private event EventHandler<MyArgs> _OnChange; private event EventHandler<MyArgs> OnChange { add { lock (_Lock) { _OnChange += value; } } remove { lock (_Lock) { _OnChange -= value; } } } // public void RaiseEvent() { EventHandler<MyArgs> lHandler = _OnChange; if (lHandler == null) return; lHandler(this, new MyArgs(0)); }// } // class
It is clear that events are not delegates. They restrict access rights from outside of the event class. To describe events you could most likely say that they are wrappers around delegates.
Whenever an exception is thrown during an event call then all following calls will not be executed. And it is tricky to determine which calls were not executed, because the order of event calls is not guaranteed to be in sequence. In fact it does execute in sequence, but there is no guarantee.
static void EventExceptions1() { MyActionEvent3 lEvent = new MyActionEvent3(); lEvent.OnChange += (sender, e) => Console.WriteLine("Executed subscription 1"); lEvent.OnChange += (sender, e) => { throw new Exception("OMG!"); }; lEvent.OnChange += (sender, e) => Console.WriteLine("Executed subscription 3"); lEvent.RaiseEvent(); } //
So you have to deal with exceptions manually if you want to satisfy/execute as many event subscriptions as possible. You could add a try/catch block for each subscription. Or you could invoke the InvocationList yourself by calling the GetInvocationList() method [System.Delegate] and execute each item manually in a try/catch block. Let’s have a look at the following practical solution:
public class MyActionEvent6 { public event EventHandler OnChange = delegate { }; public void RaiseEvent() { List<Exception> lExceptions = new List<Exception>(); foreach (Delegate lHandler in OnChange.GetInvocationList()) { try { lHandler.DynamicInvoke(this, EventArgs.Empty); } catch (Exception ex) { lExceptions.Add(ex); } } if (lExceptions.Count > 0) throw new AggregateException(lExceptions); }// } // class static void EventExceptions6() { MyActionEvent6 lEvent = new MyActionEvent6(); lEvent.OnChange += (sender, e) => Console.WriteLine("Executed subscription 1"); lEvent.OnChange += (sender, e) => { throw new Exception("OMG!"); }; lEvent.OnChange += (sender, e) => Console.WriteLine("Executed subscription 3"); try { lEvent.RaiseEvent(); } catch (AggregateException ex) { foreach (Exception lException in ex.InnerExceptions) { Console.WriteLine(lException.InnerException.Message); } } } //
Events (part 1, advanced)
Events link code dynamically together via subscriptions. There are many ways to achieve this. To better understand events, we will start with the old school approach. It is pretty much Java style. You would not do such in C#.
public interface IListener { void OnEvent(int xDummy); } // interface public class AnyClass { private List<Ilistener> _Listeners = new List<Ilistener>(); public void AddListener(IListener xListener) { lock (_Listeners) { _Listeners.Add(xListener); } } // public void RemoveListener(IListener xListener) { lock (_Listeners) { _Listeners.Remove(xListener); } } // protected void RaiseEvent() { lock (_Listeners) { foreach (IListener lListener in _Listeners) lListener.OnEvent(0); } }// public void DoSomeCalc() { Console.WriteLine("calculating something"); Console.WriteLine("done"); Console.WriteLine("going to tell others"); RaiseEvent(); } // } // class public class MyLittleProgram : IListener { public void StartDemo() { AnyClass c = new AnyClass(); c.AddListener(this); c.DoSomeCalc(); c.RemoveListener(this); } // public void OnEvent(int xDummy) { Console.WriteLine("Hey, cool! I got a notification"); } // } // class static void Main(string[] args) { MyLittleProgram p = new MyLittleProgram(); p.StartDemo(); Console.ReadLine(); } //
Above example program uses a List that stores classes. An event is raised by iterating through that list and calling the desired method. This is a lot of code for something really simple.
In the post “Delegates (basics)” https://csharphardcoreprogramming.wordpress.com/2013/12/16/delegates-basics/ we have learned to use delegates. And let’s emphasize it again: We are talking about MulticastDelegates. In the post “Lambda expressions (advanced)” https://csharphardcoreprogramming.wordpress.com/2013/12/17/lambda-expressions-advanced/ we then came across Func and Action. The next step would be to make use of Action.
public class MyActionEvent { public Action OnChange { get; set; } public void RaiseEvent() { Action lAction = OnChange; if (lAction == null) return; lAction(); }// } // class static void Main(string[] args) { MyActionEvent lEventClass = new MyActionEvent(); lEventClass.OnChange += () => Console.WriteLine("I got a notification."); lEventClass.OnChange += () => Console.WriteLine("Me too."); lEventClass.RaiseEvent(); Console.ReadLine(); } //
Notice that we used a local delegate variable (Action lAction = OnChange;) and did not call OnEvent directly. This is important in a multithreaded environment. Allthough it is unlikely, the event could still turn null before it gets raised.
Above code is much more legible now. But it is really bad practice. The event is accessible from outside. The class MyActionEvent loses control over the event. The Action OnChange is accessible and can be raised from outside the class. This is why C# implemented the “event” keyword. Code no longer uses public properties but public event fields, which are properly protected.
An event can only be assigned by using “+=”, whereas an Action can also be entirely overridden with “=”. It makes programming a little bit safer, you don’t run the risk of removing all previous subscriptions by mistake. Also events can only be raised by code within the same class. There is no way events could be raised from outside.
To make your life easier you can assign an empty delegate during the initialization process. By using “= delegate { };” there is no further need to check for the null condition, especially that no outside code can assign null to the event. Only code inside the class can assign null. Therefore the class is in full control of the event.
public class MyActionEvent2 { public event Action OnChange = delegate { }; public void RaiseEvent() { OnChange(); Console.WriteLine("number of event handlers: " + OnChange.GetInvocationList().Length); }// } // class
Personally I do not like the approach with empty delegates. The number of delegates is artificially increased by one. A null check is pretty much using no time at all, so why should someone prefer to call an empty method each time an event is raised?
The next step is to replace the dirty Action by a proper delegate definition, which is EventHandler<>. By default its parameters are the sender object and some event arguments. This is a C# convention and should be followed. Surely you are familiar with such event calls from the Winforms/WPF environment.
public class MyArgs : EventArgs { public MyArgs(int xValue) { Value = xValue; } // constructor public int Value { get; set; } } // class public class MyActionEvent3 { public event EventHandler<myargs> OnChange; public void RaiseEvent() { EventHandler<myargs> lHandler = OnChange; if (lHandler == null) return; lHandler(this, new MyArgs(0)); }// } // class static void Main(string[] args) { MyActionEvent3 lEventClass = new MyActionEvent3(); lEventClass.OnChange += (sender, e) => Console.WriteLine("Event raised, field value is: " + e.Value); lEventClass.RaiseEvent(); Console.ReadLine(); } //