Thursday, June 11, 2020

C++ example: custom class type used in unordered_map

C++ example: custom class type used in unordered_map

To use a custom class type as key in unordered_map, we need to overload the == operator and define custom hash function for this type. See the below example.

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

class Student{
public:
    Student(const string& name, int age):name(name), age(age){}

    friend bool operator==(const Student& lhs, const Student& rhs){
        return lhs.name == rhs.name && lhs.age == rhs.age;
    }

    void output() const{
        cout<<"Name "<<name<<" Age "<<age<<endl;
    }

public:
    string name;
    int age;
};

struct hashStudent{
    std::size_t operator()(const Student& stu) const{
        return hash<string>()(stu.name)| (hash<int>()(stu.age)<<1);
    }
};

int main(){
    unordered_map<Student, int, hashStudent> m;
    m.emplace(Student{"John", 21}, 4);
    m.emplace(Student{"John", 12}, 3);
    m.emplace(Student{"Ann", 24}, 2);

    for(const auto& [key, val]: m){
        cout<<key.name<<" "<<key.age<<" "<<val<<endl;
    }
    return 0;
}

Saturday, June 6, 2020

C++ priority queue custom comparator

C++ priority queue custom comparator

Method1

#include <iostream>
#include <string>
#include <vector>
#include <queue>

using namespace std;

class Student{
public:
	Student(const string& name, int age):name(name), age(age){}

	friend bool operator<(const Student& lhs, const Student& rhs){
		if(lhs.name == lhs.name){
			return lhs.age < rhs.age;
		}
		return lhs.name < rhs.name;		
	}

	void output() const{
		cout<<"Name "<<name<<" Age "<<age<<endl;
	}

public:
	string name;
	int age;
};

int main(){
	priority_queue<Student> q;
	q.push(Student{"John", 21});
	q.push(Student{"John", 12});
	q.push(Student{"Ann", 24});

	while(!q.empty()){
		q.top().output();
		q.pop();
	}
	return 0;
}

Method2

#include <iostream>
#include <string>
#include <vector>
#include <queue>

using namespace std;

class Student{
public:
	Student(const string& name, int age):name(name), age(age){}

	bool operator<(const Student& other) const{
		if(name == other.name){
			return age < other.age;
		}
		return name < other.name;
	}

	void output() const{
		cout<<"Name "<<name<<" Age "<<age<<endl;
	}

public:
	string name;
	int age;
};

int main(){
	priority_queue<Student> q;
	q.push(Student{"John", 21});
	q.push(Student{"John", 12});
	q.push(Student{"Ann", 24});

	while(!q.empty()){
		q.top().output(); 
		q.pop();
	}
	return 0;
}

Method3

#include <iostream>
#include <string>
#include <vector>
#include <queue>

using namespace std;

class Student{
public:
	Student(const string& name, int age):name(name), age(age){}

	void output() const{
		cout<<"Name "<<name<<" Age "<<age<<endl;
	}

public:
	friend struct comp;

	string name;
	int age;
};

struct comp{
	bool operator()(const Student& lhs, const Student& rhs){
		if(lhs.name == lhs.name){
			return lhs.age < rhs.age;
		}
		return lhs.name < rhs.name;				
	}
};

int main(){
	priority_queue<Student, vector<Student>, comp> q;
	q.push(Student{"John", 21});
	q.push(Student{"John", 12});
	q.push(Student{"Ann", 24});

	while(!q.empty()){
		q.top().output(); 
		q.pop();
	}
	return 0;
}

Method4*

#include <iostream>
#include <string>
#include <vector>
#include <queue>

using namespace std;

class Student{
public:
	Student(const string& name, int age):name(name), age(age){}

	void output() const{
		cout<<"Name "<<name<<" Age "<<age<<endl;
	}

public:
	string name;
	int age;
};

bool comp(const Student& lhs, const Student& rhs){
	if(lhs.name == lhs.name){
		return lhs.age < rhs.age;
	}
	return lhs.name < rhs.name;				
}

int main(){
	priority_queue<Student, vector<Student>, std::function<bool(const Student&, const Student&)>> q(comp);
	q.push(Student{"John", 21});
	q.push(Student{"John", 12});
	q.push(Student{"Ann", 24});

	while(!q.empty()){
		q.top().output(); 
		q.pop();
	}
	return 0;
}

Method5

#include <iostream>
#include <string>
#include <vector>
#include <queue>

using namespace std;

class Student{
public:
	Student(const string& name, int age):name(name), age(age){}

	void output() const{
		cout<<"Name "<<name<<" Age "<<age<<endl;
	}

public:
	string name;
	int age;
};

int main(){

	auto comp = [](const Student& lhs, const Student& rhs)->bool{
		if(lhs.name == lhs.name){
			return lhs.age < rhs.age;
		}
		return lhs.name < rhs.name;	
	};

	priority_queue<Student, vector<Student>, decltype(comp)> q(comp);
	q.push(Student{"John", 21});
	q.push(Student{"John", 12});
	q.push(Student{"Ann", 24});

	while(!q.empty()){
		q.top().output(); 
		q.pop();
	}
	return 0;
}

Thursday, May 28, 2020

C++ keywords, explicit, default, delete, noexcept, override, and final

C++ keywords, explicit, default, delete, noexcept, override, and final

explicit
prevent use for implicit conversion in side a class

for example

explicit Array(int size);

This code prevent implicit conversion from int to Array class

default
Let compiler generate default functions.

delete
Remove the default implementation of a method

Array(const Array&) = delete;

for example, the above code prevents C++ from automatically creating copy constructor

noexcept
Optimize the code without worrying about exception. Does not guarantee there is no exceptions throw in the function.

override
Tell a virtual function it must override a function in the base class. Preventing override with different parameters.

final
disallow inheritance from class or function
for example

struct Base final{
}

Base struct cannot be inherited.

Thursday, May 21, 2020

R Parallel Writing to Files

R Parallel Writing to Files

Here I will use foreach and doParallel library to demonstrate how to parallel write to file.

library(stringr)
library(flock)
library(foreach)
library(doParallel)
cl <- makeCluster(detectCores(), outfile = "a.out")
registerDoParallel(cl)
lock0 <-tempfile()
foreach (
  i = 1:10000,
  .combine = cbind,
  .packages = c('stringr', 'flock'),
  .export = ls(globalenv())
) %dopar% {
  locked0 <- flock::lock(lock0)
  write(i,file="outfile.txt"),append=TRUE) 
  flock::unlock(locked0)
}
stopCluster(cl)

The makeCluster(detectCores(), outfile = "a.out") statement make a cluster by using the all available cores, and the console output will be direct to a.out file.

The statement registerDoParallel(cl) register the cluster as the foreach parallel backend.
Note, int the foreach statements, we have .packages = c('stringr', 'flock') and .export = ls(globalenv(). The former exposes the specified packages to the context inside the foreach loop and the latter exposes all the declared variable to the foreach loop. Without this, the inside foreach loop cannot see the outside library or variables.

To avoid data race problem when multiple processes/threads writing to the same file, we use flock library as a mutex and wrap the write operation by flock::lock and flock::unlock.

Using mutex can make the processing really slow. The other way to do this is that each process write to its separate file. You can use the process id in the file name. For example,

write(i,file=paste(c("outfile",Sys.getpid(),".txt"), collapse =""),append=TRUE)

One thing to notice is that, if you parallel processing include database connections, the above code will fail since the parallel process cannot spawn the database connections. You can use the below code initialize the connections when build the cluster using clusterEvalQ.

library(RODBC)   #use the ODBC library
library(DBI)
library(odbc)
odbcCloseAll()
library(foreach)
library(doParallel)
cl <- makeCluster(detectCores(), outfile = "a.out")
clusterEvalQ(cl, {
   library(odbc)
   library(RODBC)
   library(DBI)
   dbname1 = "test"  # change this when change server!!!
   channel1 = RODBC::odbcConnect(dbname1)
   con1 <- DBI::dbConnect(odbc(),  dbname1)
})
registerDoParallel(cl)

Visual Studio 2017 SSIS project incompatible

Visual Studio 2017 SSIS project incompatible

Open Visual Studio 2017, select Tools–> Extensions and Updates. Click Online in the left pane, search “Microsoft Reporting Services Projects”. Then click install. You need to close Visual Studio to let the installation begin. When it is done, Tools–> Extensions and Updates. Click Installed in the left pane, search “Microsoft Reporting Services Projects”. Click Enable.
Right click the incompatible project, click Reload. This should solve the problem.

Monday, May 18, 2020

C++ avoid arithmetic operation on size_t time

C++ avoid arithmetic operation on size_t time

Here is a simple test code about this topic

#include <typeinfo>

    string s= "a";
    int i = 0;
    cout<<i<<" "<<typeid(i).name()<<endl;
    cout<<s.length()<<" "<<typeid(s.length()).name()<<endl;
    cout<<i - s.length()<<" "<<typeid(i - s.length()).name()<<endl;

The output is a very large number (18446744073709551615) instead of -1 as intended (see below).

0 i
1 m
18446744073709551615 m

As the type of s.length() is size_t. size_t is unsigned int or unsigned long depending on the machine used. It seems that the compiler converts the value to an unsigned long type.
To avoid this kind of problem, using something int n = s.length(); and then use this variable to do the calculations.

Sunday, May 17, 2020

Is it safe to delete a pointer to nullptr

Is it safe to delete a pointer to nullptr

I did a test on a online compiler(online compiler) with the below code.

#include <iostream>

using namespace std;

int main()
{
    int * a = nullptr;
    delete a;
    return 0;
}

The program compiles and runs with no problem. So it is safe to delete pointer too a nullptr. However, you can not execute delete nullptr; directly since nullptr is a pointer literal.