Saturday, May 04, 2013

And here is the same thing using variadic template arguments, a new feature of C++11:



template class base_t
{
public:
virtual R operator()(Args... p) { return R(); }
};

template class stub_t : public base_t
{
T* _obj;
union
{
R (T::*_ptr)(Args...);
R (T::*_ptr_c)(Args...) const;
};

public:
stub_t(T* obj, R (T::*ptr)(Args...))
: _obj(obj), _ptr(ptr)
{
}
stub_t(T* obj, R (T::*ptr)(Args...) const)
: _obj(obj), _ptr_c(ptr)
{
}
R operator()(Args... p)
{
return (_obj->*_ptr)(p...);
}
};

template class signal_t
{
R(*_ptr)(Args...);
base_t* _stub;

public:
signal_t() : _ptr(nullptr), _stub(nullptr)
{
}
void connect(R(*ptr)(Args... args))
{
_ptr = ptr;
};

template void connect(T& obj, R(T::*ptr)(Args...))
{
_stub = new stub_t(&obj, ptr);
}
template void connect(T* obj, R(T::*ptr)(Args...) )
{
_stub = new stub_t(obj, ptr);
}
template void connect(T& obj, R(T::*ptr)(Args...) const)
{
_stub = new stub_t(&obj, ptr);
}
template void connect(T* obj, R(T::*ptr)(Args...) const)
{
_stub = new stub_t(obj, ptr);
}

R operator()(Args... p)
{
if(_ptr)
return _ptr(p...);
else if(_stub)
return (*_stub)(p...);
else
return R();
}
};


Saves a few hundred lines of code, as well as reducing the number of places bugs can crop up.

Friday, May 03, 2013

Here is some signal/slot stuff that I've recently put together:


template class base
{
public:
virtual R call(const A1& arg1 = A1(), const A2& arg2 = A2(), const A3& arg3 = A3(), const A4& arg4 = A4())
{ return (R)(arg1, arg2, arg3, arg4); }
};

template class base
{
public:
virtual R call() { return R(); }
};

template class base
{
public:
virtual R call(const A1& arg1 = A1()) { return R(); }
};

template class base
{
public:
virtual R call(const A1& arg1 = A1(), const A2& arg2 = A2()) { return R(); }
};

template class base
{
public:
virtual R call(const A1& arg1 = A1(), const A2& arg2 = A2(), const A3& arg3 = A3()) { return R(); }
};



template 
class stub : public base
{
public:
stub(T& obj, R (T::*ptr)(A1, A2, A3, A4)) : _obj(obj), _ptr(ptr) { }
R call(const A1& arg1 = A1(), const A2& arg2 = A2(), const A3& arg3 = A3(), const A4& arg4 = A4())
{
return (_obj.*_ptr)(arg1, arg2, arg3, arg4);
}
private:
T& _obj;
R (T::*_ptr)(A1, A2, A3, A4);
};


template class stub : public base
{
public:
stub(T& obj, R (T::*ptr)()) : _obj(obj), _ptr(ptr) { }
R call()
{
return (_obj.*_ptr)();
}
private:
T& _obj;
R (T::*_ptr)();
};

template class stub : public base
{
public:
stub(T& obj, R (T::*ptr)(A1)) : _obj(obj), _ptr(ptr) { }
R call(const A1& arg1 = A1())
{
return (_obj.*_ptr)(arg1);
}
private:
T& _obj;
R (T::*_ptr)(A1);
};

template class stub : public base
{
public:
stub(T& obj, R (T::*ptr)(A1, A2)) : _obj(obj), _ptr(ptr) { }
R call(const A1& arg1 = A1(), const A2& arg2 = A2())
{
return (_obj.*_ptr)(arg1, arg2);
}
private:
T& _obj;
R (T::*_ptr)(A1, A2);
};

template class stub : public base
{
public:
stub(T& obj, R (T::*ptr)(A1, A2, A3)) : _obj(obj), _ptr(ptr) { }
R call(const A1& arg1 = A1(), const A2& arg2 = A2(), const A3& arg3 = A3())
{
return (_obj.*_ptr)(arg1, arg2, arg3);
}
private:
T& _obj;
R (T::*_ptr)(A1, A2, A3);
};


template class signal_t
{
public:
signal_t() : _ptr(0), _stub(0) { }
template void connect(T1& object, R (T1::*slot)(A1, A2, A3, A4))
{
_stub = new stub(object, slot);
}
template void connect(T1& object, R (T1::*slot)(A1, A2, A3, A4) const)
{
_stub = new stub(object, slot);
}
void connect(R (*function)(A1, A2, A3, A4)) { _ptr = function; }
R operator()(const A1& arg1 = A1(), const A2& arg2 = A2(), const A3& arg3 = A3(), const A4& arg4 = A4()) 
{
if(_ptr)
return _ptr(arg1, arg2, arg3, arg4);
else if(_stub)
return _stub->call(arg1, arg2, arg3, arg4);
else
return R();
}
private:
R (*_ptr)(A1, A2, A3, A4);
base* _stub;
};

 template class signal_t
{
public:
signal_t() : _ptr(0), _stub(0) { }
template void connect(T1& object, R (T1::*slot)())
{
_stub = new stub(object, slot);
}
template void connect(T1& object, R (T1::*slot)() const)
{
_stub = new stub(object, slot);
}
void connect(R (*function)()) { _ptr = function; }
R operator()() 
{
if(_ptr)
return _ptr();
else if(_stub)
return _stub->call();
else
return R();
}
private:
R (*_ptr)();
base* _stub;
};

 template class signal_t
{
public:
signal_t() : _ptr(0), _stub(0) { }
template void connect(T1& object, R (T1::*slot)(A1))
{
_stub = new stub(object, slot);
}
template void connect(T1& object, R (T1::*slot)(A1) const)
{
_stub = new stub(object, slot);
}
void connect(R (*function)(A1)) { _ptr = function; }
R operator()(const A1& arg1 = A1()) 
{
if(_ptr)
return _ptr(arg1);
else if(_stub)
return _stub->call(arg1);
else
return R();
}
private:
R (*_ptr)(A1);
base* _stub;
};

template class signal_t
{
public:
signal_t() : _ptr(0), _stub(0) { }
template void connect(T1& object, R (T1::*slot)(A1, A2))
{
_stub = new stub(object, slot);
}
template void connect(T1& object, R (T1::*slot)(A1, A2) const)
{
_stub = new stub(object, slot);
}
void connect(R (*function)(A1, A2)) { _ptr = function; }
R operator()(const A1& arg1 = A1(), const A2& arg2 = A2()) 
{
if(_ptr)
return _ptr(arg1, arg2);
else if(_stub)
return _stub->call(arg1, arg2);
else
return R();
}
private:
R (*_ptr)(A1, A2);
base* _stub;
};

template class signal_t
{
public:
signal_t() : _ptr(0), _stub(0) { }
template void connect(T1& object, R (T1::*slot)(A1, A2, A3))
{
_stub = new stub(object, slot);
}
template void connect(T1& object, R (T1::*slot)(A1, A2, A3) const)
{
_stub = new stub(object, slot);
}
void connect(R (*function)(A1, A2, A3)) { _ptr = function; }
R operator()(const A1& arg1 = A1(), const A2& arg2 = A2(), const A3& arg3 = A3()) 
{
if(_ptr)
return _ptr(arg1, arg2, arg3);
else if(_stub)
return _stub->call(arg1, arg2, arg3);
else
return R();
}
private:
R (*_ptr)(A1, A2, A3);
base* _stub;
};


Not sure exactly why it works but it seems to.

Friday, April 03, 2009

Here is the full proposal I wrote applying to Google SoC:

I want to write an IM client based on libpurple using the Qt GUI library. This would provide a native interface on Windows comparable to using MFC or .NET while still retaining possible cross-platform compatibility. The difficult part of the project will be the scope of the task. Rather than attempt to complete an entire client single-handedly over one summer, I would rather lay the foundation for future completion: have most of the basic functionality working (buddy list, multiple accounts, basic messaging), along with a clear path to flesh out the rest of the code. The proposed timeline looks like:
Week 1 : set up build environment, get libpurple compiling under Visual C++, bootstrap project
Weeks 2-4 : account management, handling multiple accounts, status updates, etc
Weeks 5-6 : buddy (list) management, buddy info, etc
Weeks 7-8 : message management, emoticons, possibly file transfer
Any additional time: clean up code, document code, prepare for potential future code contributors.

I like this project because I have used Gaim/Pidgin as well as Adium for several years and I am excited about helping to give Pidgin a face lift on Windows. I have a couple of GPL'd projects up on sourceforge.net, including a klondike solitaire game for Mac OS X ("MacSolitaireX") and a basic desktop search application for Windows ("seek"). I spent two semesters working on a creative inquiry project at Clemson specifically focused on open-source software development. My preferred development environment is Visual Studio and my favorite language is C++. I will do an amazing job finishing this project because I have experience writing an instant messaging client on my own time (although not specifically using libpurple) and I enjoy spending long hours programming late into the night :)

Saturday, September 20, 2008

I wrote a neat little threading class to make spawning off new threads a lot easier, especially when you want to spawn off a class member function on a new thread. The syntax is as simple as it could possibly be:

class my_class
{
void member_function(int argument1, float argument2) { ... }
};

{
...
int arg1;
float arg2;

my_class c;
thread_t thread;
thread.spawn_thread(c, &my_class::member_function, arg1, arg2);
}


And that's it. Plus, you can spawn a thread from a non-member function, too. It works under both Windows threads and pthreads. The main limitation is that it currently only supports up to 4 arguments, but it should be fairly simple to edit the code should you require more. Let me know if (or rather, when) you spot any bugs. Here's the whole code listing, should you want to use it:


#include
#ifndef THREAD_H
#define THREAD_H

#ifdef WIN32
#include
#define CALLING_CONVENTIONS WINAPI
#else
#include
#include
#define CALLING_CONVENTIONS
#endif

//cbigart

class thread_t
{
public:
const static int MAX_NUM_ARGS = 4;

static void sleep(int ms)
{
#ifdef WIN32
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}

private:
template
static RT CALLING_CONVENTIONS method_stub1(SA arg)
{
struct args_t
{
R (*func_ptr)(A1);
A1 arg1val;
};
args_t args = *(args_t*)arg;
(args.func_ptr)(args.arg1val);
return RT();
}
template
static RT CALLING_CONVENTIONS method_stub2(SA arg)
{
struct args_t
{
R (*func_ptr)(A1, A2);
A1 arg1val;
A2 arg2val;
};
args_t args = *(args_t*)arg;
(args.func_ptr)(args.arg1val, args.arg2val);
return RT();
}
template
static RT CALLING_CONVENTIONS method_stub3(SA arg)
{
struct args_t
{
R (*func_ptr)(A1, A2, A3);
A1 arg1val;
A2 arg2val;
A3 arg3val;
};
args_t args = *(args_t*)arg;
(args.func_ptr)(args.arg1val, args.arg2val, args.arg3val);
return RT();
}
template
static RT CALLING_CONVENTIONS method_stub4(SA arg)
{
struct args_t
{
R (*func_ptr)(A1, A2, A3, A4);
A1 arg1val;
A2 arg2val;
A3 arg3val;
A4 arg4val;
};
args_t args = *(args_t*)arg;
(args.func_ptr)(args.arg1val, args.arg2val, args.arg3val, args.arg4val);
return RT();
}

public:
template void spawn_thread(R (*func_ptr)())
{
#ifdef WIN32
LPTHREAD_START_ROUTINE f = (LPTHREAD_START_ROUTINE)func_ptr;;
CreateThread(0, 0, f, 0, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, func_ptr, 0);
#endif
}
template void spawn_thread(R (*func_ptr)(A1 arg1), const A1& arg1val = A1())
{
struct args_t
{
R (*func_ptr)(A1);
A1 arg1val;
};
args_t* args = new args_t;
args->arg1val = arg1val;
args->func_ptr = func_ptr;
#ifdef WIN32
CreateThread(0, 0, &method_stub1, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &method_stub1, args);
#endif
}
template void spawn_thread(R (*func_ptr)(A1 arg1, A2 arg2),
const A1& arg1val = A1(), const A2& arg2val = A2())
{
struct args_t
{
R (*func_ptr)(A1, A2);
A1 arg1val;
A2 arg2val;
};
args_t* args = new args_t;
args->arg1val = arg1val;
args->arg2val = arg2val;
args->func_ptr = func_ptr;
#ifdef WIN32
CreateThread(0, 0, &method_stub2, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &method_stub2, args);
#endif
}
template void spawn_thread(R (*func_ptr)(A1 arg1, A2 arg2, A3 arg3),
const A1& arg1val = A1(), const A2& arg2val = A2(), const A3& arg3val = A3())
{
struct args_t
{
R (*func_ptr)(A1, A2, A3);
A1 arg1val;
A2 arg2val;
A3 arg3val;
};
args_t* args = new args_t;
args->arg1val = arg1val;
args->arg2val = arg2val;
args->arg3val = arg3val;
args->func_ptr = func_ptr;
#ifdef WIN32
CreateThread(0, 0, &method_stub3, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &method_stub3, args);
#endif
}
template void spawn_thread(R (*func_ptr)(A1 arg1, A2 arg2, A3 arg3, A4 arg4),
const A1& arg1val = A1(), const A2& arg2val = A2(), const A3& arg3val = A3(), const A4& arg4val = A4())
{
struct args_t
{
R (*func_ptr)(A1, A2, A3, A4);
A1 arg1val;
A2 arg2val;
A3 arg3val;
A4 arg4val;
};
args_t* args = new args_t;
args->arg1val = arg1val;
args->arg2val = arg2val;
args->arg3val = arg3val;
args->arg4val = arg4val;
args->func_ptr = func_ptr;
#ifdef WIN32
CreateThread(0, 0, &method_stub4, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &method_stub4, args);
#endif
}

private:
template
static RT CALLING_CONVENTIONS class_method_stub(SA arg)
{
struct args_t
{
T* cptr;
R (T::*func_ptr)();
};
args_t args = *(args_t*)arg;
(*args.cptr.*args.func_ptr)();
return RT();
}
template
static RT CALLING_CONVENTIONS class_method_stub1(SA arg)
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1);
A1 arg1val;
};
args_t args = *(args_t*)arg;
(*args.cptr.*args.func_ptr)(args.arg1val);
return RT();
}
template
static RT CALLING_CONVENTIONS class_method_stub2(SA arg)
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1, A2);
A1 arg1val;
A2 arg2val;
};
args_t args = *(args_t*)arg;
(*args.cptr.*args.func_ptr)(args.arg1val, args.arg2val);
return RT();
}
template
static RT CALLING_CONVENTIONS class_method_stub3(SA arg)
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1, A2, A3);
A1 arg1val;
A2 arg2val;
A3 arg3val;
};
args_t args = *(args_t*)arg;
(*args.cptr.*args.func_ptr)(args.arg1val, args.arg2val, args.arg3val);
return RT();
}
template
static RT CALLING_CONVENTIONS class_method_stub4(SA arg)
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1, A2, A3, A4);
A1 arg1val;
A2 arg2val;
A3 arg3val;
A4 arg4val;
};
args_t args = *(args_t*)arg;
(*args.cptr.*args.func_ptr)(args.arg1val, args.arg2val, args.arg3val, args.arg4val);
return RT();
}

public:
template void spawn_thread(T& class_instance,
R (T::*func_ptr)())
{
struct args_t
{
T* cptr;
R (T::*func_ptr)();
};
args_t* args = new args_t;
args->cptr = &class_instance;
args->func_ptr = func_ptr;
#ifdef WIN32
CreateThread(0, 0, &class_method_stub, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &class_method_stub, i);
#endif
}
template void spawn_thread(T& class_instance,
R (T::*func_ptr)(A1), const A1& arg1val = A1())
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1);
A1 arg1val;
};
args_t* args = new args_t;
args->cptr = &class_instance;
args->func_ptr = func_ptr;
args->arg1val = arg1val;
#ifdef WIN32
CreateThread(0, 0, &class_method_stub1, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &class_method_stub1, i);
#endif
}
template void spawn_thread(T& class_instance,
R (T::*func_ptr)(A1, A2), const A1& arg1val = A1(), const A2& arg2val = A2())
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1, A2);
A1 arg1val;
A2 arg2val;
};
args_t* args = new args_t;
args->cptr = &class_instance;
args->func_ptr = func_ptr;
args->arg1val = arg1val;
args->arg2val = arg2val;
#ifdef WIN32
CreateThread(0, 0, &class_method_stub2, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &class_method_stub2, i);
#endif
}
template void spawn_thread(T& class_instance,
R (T::*func_ptr)(A1, A2, A3), const A1& arg1val = A1(), const A2& arg2val = A2(), const A3& arg3val = A3())
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1, A2, A3);
A1 arg1val;
A2 arg2val;
A3 arg3val;
};
args_t* args = new args_t;
args->cptr = &class_instance;
args->func_ptr = func_ptr;
args->arg1val = arg1val;
args->arg2val = arg2val;
args->arg3val = arg3val;
#ifdef WIN32
CreateThread(0, 0, &class_method_stub3, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &class_method_stub3, i);
#endif
}
template void spawn_thread(T& class_instance,
R (T::*func_ptr)(A1, A2, A3, A4), const A1& arg1val = A1(), const A2& arg2val = A2(), const A3& arg3val = A3(), const A4& arg4val = A4())
{
struct args_t
{
T* cptr;
R (T::*func_ptr)(A1, A2, A3, A4);
A1 arg1val;
A2 arg2val;
A3 arg3val;
A4 arg4val;
};
args_t* args = new args_t;
args->cptr = &class_instance;
args->func_ptr = func_ptr;
args->arg1val = arg1val;
args->arg2val = arg2val;
args->arg3val = arg3val;
args->arg4val = arg4val;
#ifdef WIN32
CreateThread(0, 0, &class_method_stub4, args, 0, 0);
#else
pthread_t thread;
pthread_create(&thread, 0, &class_method_stub4, i);
#endif
}

};

#endif

Tuesday, February 13, 2007

It's been a while since I posted last. My latest project deals with manipulating images using pixel shaders. I have a few other side things that I'm working on too... but for the most part they are all on the "back burner": I plan to work on them eventually, but they aren't getting anywhere at the moment.

I've been reading a bit on garbage collection schemes. Performance is a big issue for me, and the worst thing that can happen is that your program hard faults: portions of either its code or data have been paged out to the disk and need to be paged back in to memory. Paging is the number one performance killer on today's desktop systems. Throwing more ram at the problem is one solution, but writing more memory-efficient code is a better solution. I'm very curious to see what kinds of memory usage patterns common programs have, I just need to find (or write) a good tool to help visualize memory usage.

Qt is a great library, and I use it in almost all my GUI-based projects. That's not to say it's perfect, though. I run into some problems using it pretty frequently. Usually it is nothing major, but bugs are always frustrating. I should look into some Qt-specific forums or mailing lists to see if I can get some community support on some of these issues.

A lot of Qt's "shortcomings" stem mainly from a difference of opinion on design. Qt takes a pretty "heavy" or "fat" object design, a lot like Java. The library is designed to add a lot of advanced features that C++ lacks by default, like introspection, object names and run-time type information. Signals and slots are pretty hefty, too. All this baggage might not be much of a performance bottleneck, especially in CPU time... in that regard it's a non-issue. However, there are a lot of extra dynamic memory allocations in there that aren't necessary in a more lightweight object/class design. I do prefer, in general, static linking and inline functions, which are both optimized for a more-memory-for-more-speed trade off. However, I think that data size is where the problem is, not code size. Of course, I need to look into this claim with some tools to profile memory usage before I really come to a full conclusion.

Sunday, June 25, 2006

I'm working on some templated container classes. My goal is to eliminate as much overhead as possible. Consider the following assembler generated:

53: a.allocate(10);
004010C1 mov esi,dword ptr [__imp__malloc (40823Ch)]
push 28h
004010C9 mov dword ptr [a+4 (407134h)],0Ah
004010D3 call esi
54: len = 10;
55: ba = (int*) malloc(sizeof(int)*len);
004010D5 push 28h
004010D7 mov dword ptr [a (407130h)],eax
004010DC mov dword ptr [len (40712Ch)],0Ah
004010E6 call esi
56:
57: a[3] = 3;
004010E8 mov ecx,dword ptr [a (407130h)]
004010EE mov dword ptr [ba (407128h)],eax
004010F3 mov eax,3
004010F8 mov dword ptr [ecx+0Ch],eax
58: ba[3] = 3;
004010FB mov edx,dword ptr [ba (407128h)]
00401101 add esp,8
00401104 mov dword ptr [edx+0Ch],eax


Now, when we move the instructions out of order and group them according to which variables they are operating on, and add some comments...:

004010C1 mov esi,dword ptr [__imp__malloc (40823Ch)] ;move addr of malloc into a register

53: a.allocate(10);
mov dword ptr [a+4 (407134h)],0Ah ;put value of 10 into a._size

push 28h ;push value of 40 onto the stack
call esi ;call malloc with arg of 40

mov dword ptr [a (407130h)],eax ;move ret value of malloc into a._begin


54: len = 10;

mov dword ptr [len (40712Ch)],0Ah ;put value of 10 into len

55: ba = (int*) malloc(sizeof(int)*len);
push 28h
;push value of 40 onto the stack
call esi
;call malloc with arg of 40
mov dword ptr [ba (407128h)],eax ;put ret value of malloc into ba


;;;

mov eax,3 ;move value of 3 into a register

57: a[3] = 3;
mov ecx,dword ptr [a (407130h)] ;move address of a._begin into register
mov dword ptr [ecx+0Ch],eax ;move value of eax (3) into a._begin+3

58: ba[3] = 3;
mov edx,dword ptr [ba (407128h)] ;move address of ba into register
mov dword ptr [edx+0Ch],eax
;move value of eax (3) into ba+3


add esp,8 ;increment the stack pointer by 8



...we see that the optimized assembler generated is identical for my container class as for manually allocating an array using malloc. Since this is all wrapped up into a templated class, you are less likely to make mistakes while calling malloc. The downside is that the compiler will occasionally be able to register optimize the len variable, but in practice, you will need to keep the variable around anyway, so this isn't much of an issue.

Saturday, June 24, 2006

I've recieved several emails about MacSolitaireX, including one bug submission and a few deck backing image submissions (all from the same individual). I'm working on the next version... slowly. I've fixed the bug. It turned out that I didn't update the OpenGL context after a game had been won. What happened was that the user would win a game in vegas scoring, and the old game image would still be in the framebuffer, even though a new hand had been dealt. This would cause the user to deal again, which would cause the view to redraw, but also to subtract an additional $52 from the score.