Cold Development
A blog about software development, mobile and graphics.
Monday, 17 February 2014
Saturday, 4 May 2013
Qt 5.0 on Mac OSX - "token is not a valid binary operator in a preprocessor subexpression"
/Users/arnebef/Qt5.0.2/5.0.2/clang_64/include/QtCore/qisenum.h:53: error: token is not a valid binary operator in a preprocessor subexpression
# if __has_extension(is_enum)
~~~~~~~~~~~~~~~^
If you get the following error code when you are trying to compile a dummy project for Qt 5.0,
then you have a too old version of XCode. You have to install XCode anew (from the App Store), and then go to Xcode->Preferences->Downloads and install the command line tools. With that done you should be able to build new Qt programs.
# if __has_extension(is_enum)
~~~~~~~~~~~~~~~^
If you get the following error code when you are trying to compile a dummy project for Qt 5.0,
then you have a too old version of XCode. You have to install XCode anew (from the App Store), and then go to Xcode->Preferences->Downloads and install the command line tools. With that done you should be able to build new Qt programs.
Wednesday, 2 May 2012
How to build a software bridge
- or how software engineering projects cannot be planned as regular engineering projects
The reason these models are built up is because the actual physical step of building is so massive - which makes it very important to get the planning and design just right, since the difference between the good and bad design may mean a large monetary difference or even make the bridge fall down. And if a bad design choice shows that for example the capacity of the bridge is smaller than expected, fixing it might be very expensive and even mean that the bridge has to rebuilt.
Then lets assume we build the bridge as a software engineer would build it. The difference now is that building the bridge is free - or at least minuscule compared to the work cost of the engineer doing the design. So, you might want to build the bridge as noted earlier, doing testing, design, etc. the same way. However, with doing that you throw away a lot of the positive you get by using the fact that building the bridge is for free.
Modern software design principles exploit this principle. In the beginning of the project, the requirement process is shortened considerably. This lack of requirements are weighted by allowing new requirements to be added and existing requirements improved upon iteratively throughout the design process. When designing starts, a working bridge is built as soon as possible. It might fall down when more than two cars drive over it and it is held together with duct tape - but it is built.
And the bridge is built every time the design changes - down to every time a beam is added somewhere. And not only that, we build many bridges, and run different types of cars through them, we try to cut a cable and see what happens, maybe let the occasional asteriod hit it and see if it survives. And if one of the astroid tests suddenly fails when we change the design, we revert the design change, fix it and see if at least one of the cables hold on that asteriod impact.
Another exploitation that the free cost of building has is that the customer can try out the bridge while the design is still underway. The customer may find that the original requirement did not allow for buses, and that is improved upon. This requirement was something that the customer always intended - but when they got one of the early bridges, they tried to drive a bus across it and it falls down. As intended by the engineer ("it was never meant to do that!"), but expected by the customer. The change to the design might minimal, but the change in customer happiness is major.
The customer may also find that a new rail line has to be added beneath the car lane. This is something new that was not know when starting the project, and both the customer and engineer acknowledges this. This requires a major design change - but this is possible to do, even after the bridge was considered done, since we can simply tear the bridge down and set up a new one.
All this is not news - software used in Space Shuttle used iterative software development. And as all with all knowledge, a new generation rediscovered this and made it their own. But still companies are planning features years ahead of time, using methods that are geared against regular engineering, and not towards software engineering. It is used because it is known, it is simple, and it gives the manager much knowledge of what is happening. Using iterative methods means using methods that means more interaction, both from customers and management. But the rewards returned is definitely much higher than the work invested.
Sunday, 15 April 2012
Using offsetof to implement linked lists
offsetof is a cute litte feature in C. What the function gives is the offset that a member has in a struct. Assume we have the given struct:
typedef struct {
int a;
char c;
int b;
} s;
Offsetof would return something like this on fairly normal hardware:
offsetof(s, a); /*0 */
offsetof(s, c); /*4 */
offsetof(s, b); /*8 - no, not 5, more about that later... */
So what can this be used for? Well, a quite common use is for linked lists. A doublylinked list will often be written with two structures:
typedef struct link_t {
struct link *next_link;
struct link *prev_link;
} link;
typedef struct list_t {
struct link *first_link;
struct link *last_link;
}
Then, any structure that wants to be in a link list adds the link structure as a member:
typedef struct interesting_data {
link_t link;
int data;
} interesting_data;
To insert into a linked list, we then use a macro.
#define LIST_INSERT_BACK(list, link, member)\
do {\
char *p = (char*)link;\
link_t *l = (link_t*)((char*)link) + offsetof(typeof(link), member));\
private_insert_back_link(list, l);\
}\ while(0)
#define LIST_GET_BACK(list, link_type, member)\
\ (link_type*) private_get_back_link(list, offsetof(link_type, member));
There are of course quite a few macros needed (to traverse the list, etc.), but they are all based on using offsetof to find the offset of the list member.
Tuesday, 10 April 2012
Sizeof
So, this is the first of hopefully many mini-guides on how to do cool stuff in C...
You might usually use sizeof like this:
You might usually use sizeof like this:
int *a = malloc(sizeof(int));
But a for many little known fact about C is that sizeof will get the size of the resulting type the of the expression given as the argument. This means that this line will do the same thing as the previous line:
But a for many little known fact about C is that sizeof will get the size of the resulting type the of the expression given as the argument. This means that this line will do the same thing as the previous line:
int *a = malloc(sizeof(*a));
Since the expression *a will result in an int, the size of that expression is an int.
I went too long as a C-programmer without knowing this little fact.
Tuesday, 21 February 2012
Waiting for Raspberry Pi...
I am currently waiting in eager anticipation for the Raspberry Pi - a small low-cost ARM computer. It costs $35. Yes, $35 dollar, for a fullfledged computer that can do 1080p video decoding as well as doing OpenGL ES.
I have a few plans for this computer - a computer that had the same specs as my computer 10 years ago, but will use a 100th of both volume and power.
Too bad they did not do like other dessert-based platforms and stick an Exynos chip in there, though.
I have a few plans for this computer - a computer that had the same specs as my computer 10 years ago, but will use a 100th of both volume and power.
Too bad they did not do like other dessert-based platforms and stick an Exynos chip in there, though.
Friday, 5 August 2011
The 7 stages of refactoring
You have wanted to fix that module for ages. Just one look at it and you cringe. The documentation, the weird naming of functions, classes that are just plain weird. The module hobbeles along, but it is just plain dirty. The real programmer in you cringe, and when there finally is some time to refactor the module, you jump at it.
Step 1 - Desperation
So you start to have a real good look at what you need to. Fix a class here, rename a few functions there, tear out a few functions here. Simple, right? Well, what at first glance seemed like a simple fix, a few tweaks, now envisions itself as a monster of badly written code, ambigous comments and weird datastructures. You find members that are (seemingly) never used, functions that do the exact opposite of what they are named, etc. You cannot envision how anyone would be able to finish this is in 5 days (the time you have allocated).
Step 2 - Fixing it one line at a time
You acknowledge that this beast cannot be tackled in a single go, so you start with a part of the module and start with the small things - renaming functions, moving code blocks, create constants. You know that if nothing else you will at least not make the code worse.
Step 3 - Desperation (again)
But then, you hit a brick wall. You see the flaw in trying to reach a local minima, and see what you should do is a total rewrite. You do not have time for that, however, so you make a partial rewrite, by trying to reuse as much as you can from the old code. It will not be perfect, but you will at least try.
Step 4 - Optimism
You pound at the problem for a few days, slowly making progress and improving your code. You see how the code should be, the way the classes are bolted together. Yes, you are a few days overtime on your allotted time, but it is worth it.
Step 5 - Scram
You have used way too much time on this by now, and you feel more and more embarrassed for every standup. What should have been a simple refactoring turned into a total rewrite. And just because you wanted the code to be perfect. So you do some shortcuts, skimp on some documentation here, write it fast and dirty there. It will not be perfect, but it will do. You run the tests, and of course they all fail, but you do some quickfixes, and in the end it works fairly well. You pat yourself on the chest and do the commit.
Step 6 - Fixin time
Your rewrite was not perfect. Although it did pass its tests, they were too narrow and did not cover all the edge cases. So during a few weeks after the rewrite you apply more and more quickfixes, making the design more inelegant for each fix. It is no longer as perfect as you wanted it, but at least it is better.
Step 7 - Enlightenment
6 months passes, and you get another bugfix on the module. It has somehow gotten less pretty by the month. You see the inconsistencies and problems with the design that did not show up when you first had a look at it. And you see that what it replaced might not have been that bad in the first place, it did cover some cases that your code had not thought about. Someone on your team pushes for this to be rewritten, and you step back and let him - hoping that he too will in a few months will be enlightened.
Step 1 - Desperation
So you start to have a real good look at what you need to. Fix a class here, rename a few functions there, tear out a few functions here. Simple, right? Well, what at first glance seemed like a simple fix, a few tweaks, now envisions itself as a monster of badly written code, ambigous comments and weird datastructures. You find members that are (seemingly) never used, functions that do the exact opposite of what they are named, etc. You cannot envision how anyone would be able to finish this is in 5 days (the time you have allocated).
Step 2 - Fixing it one line at a time
You acknowledge that this beast cannot be tackled in a single go, so you start with a part of the module and start with the small things - renaming functions, moving code blocks, create constants. You know that if nothing else you will at least not make the code worse.
Step 3 - Desperation (again)
But then, you hit a brick wall. You see the flaw in trying to reach a local minima, and see what you should do is a total rewrite. You do not have time for that, however, so you make a partial rewrite, by trying to reuse as much as you can from the old code. It will not be perfect, but you will at least try.
Step 4 - Optimism
You pound at the problem for a few days, slowly making progress and improving your code. You see how the code should be, the way the classes are bolted together. Yes, you are a few days overtime on your allotted time, but it is worth it.
Step 5 - Scram
You have used way too much time on this by now, and you feel more and more embarrassed for every standup. What should have been a simple refactoring turned into a total rewrite. And just because you wanted the code to be perfect. So you do some shortcuts, skimp on some documentation here, write it fast and dirty there. It will not be perfect, but it will do. You run the tests, and of course they all fail, but you do some quickfixes, and in the end it works fairly well. You pat yourself on the chest and do the commit.
Step 6 - Fixin time
Your rewrite was not perfect. Although it did pass its tests, they were too narrow and did not cover all the edge cases. So during a few weeks after the rewrite you apply more and more quickfixes, making the design more inelegant for each fix. It is no longer as perfect as you wanted it, but at least it is better.
Step 7 - Enlightenment
6 months passes, and you get another bugfix on the module. It has somehow gotten less pretty by the month. You see the inconsistencies and problems with the design that did not show up when you first had a look at it. And you see that what it replaced might not have been that bad in the first place, it did cover some cases that your code had not thought about. Someone on your team pushes for this to be rewritten, and you step back and let him - hoping that he too will in a few months will be enlightened.
Subscribe to:
Posts (Atom)