I wasn't able to work on QubLib very much this week, and any work that I did put in went to my data structures. I did some thinking about how the STL and QubLib could work together, but that's a topic for another day when I do something with it. Today, I think I'll talk briefly about a long term goal.
One thing that I'm sure we've all noticed lately is the increase in platforms that are actually making money in today's world. Ten or fifteen years ago, for the most part everything was Windows. If you developed software that was intended to run on Windows you were pretty much guaranteed a large pool of potential customers. However, with the advent of smartphones, the game is a little different now. People still use their PCs, which now may be either a Windows or a Mac. Linux is a viable option, but I just don't ever see it becoming a mainstream operating system like Windows and OS X. However, humor me and imagine that developing for a Linux system is important. That means that you have three desktop platforms that you have to develop for. Then, people go ahead and decide that they want their smartphones to be able to communicate and share data with their desktops. Did I mention that they want to be able to do it regardless of what they're running on the desktop or their smartphone? So, if a smartphone could run Palm OS, Symbian, Android, or iOS, it doesn't matter. All of those should be able to talk with your Windows, OS X, or Linux desktop. If you approach this problem with a system agnostic data transfer approach, then maybe using bluetooth or the internet you can transfer data no problem, but data transfer isn't the real issue. The real issue is software development. How on earth are you supposed to have a system that runs on each of those desktop and smartphone platforms, and then how are you supposed to be able to keep those systems in sync with each other? Oh, and did I mention that the programs have to be written in different languages? Android runs Java, iOS runs Objective-C, and Symbian runs something... I'm not really sure yet which is the most viable option, but right now I think they operate best with Qt C++.
This is a future goal of mine. If a person develops something with QubLib in C++ and they don't use any of the special features of a language that aren't common to other languages, then it really shouldn't be too difficult to do cross-language compilation. That way, a person would be able to develop their software in Java for Android systems, and then push the button and their Objective-C and C++ versions are automatically generated. It's a long shot, but I think it's possible. I know that systems similar to this work on projects like the Unreal Engine used for game development, but those systems force the developer to program in the Unreal Scripting language, and then the system will convert the scripting language to the platform dependent code. Why can't I program in what I'm familiar in? Why can't I develop in C++ and have Java code pop out the other end?
It's days like this that I wish that I didn't have to work for a living. Anyways, that's all for today. Keep coding!
Saturday, July 9, 2011
Saturday, July 2, 2011
QubLib Continues
Work on QubLib does continue, even if I don't write about it very often. Actually there's been a lot of work going on. I want to write more in my life, which is probably why I've been keeping a journal more thoroughly lately, but I want to keep posting about QubLib too, because I feel it's a really exciting project. Anyways, here's the big thing that I've been working on lately:
Data Structures.
It seems as though I'm destined to work on these forever just because I've changed my design for them so many times, but I think I've finally hit on an architecture that pays off dividends. So, how have I designed them this time around? Let me explain with pictures!
So here's my original data structure design for a LinkedList. It's pretty simple, but that's what I miss about it. I really like the idea of manipulating objects by an explicitly declared interface, and this takes that interface to two different levels: IContainer and IList. I specifically split my interfaces this way because it allows me to share test cases across my data structures. I can talk about that another time, but my test framework works on interfaces, so I can use one set of IContainer test cases on my LinkedList, ArrayList, BinarySearchTree, and anything else that implements the IContainer interface. It's pretty cool and it's saved me a lot of time. Anyways, this was the original design. However, the more I worked with LinkedList and ArrayList, I realized that they shared a bit of code, especially things like checking bounds on the list. So, I decided that they should both inherit from an abstract base class that would implement that functionality and they could use the inherited version.
Tada! ListBase (and several other DataStructureType-Base classes) were born. It was right about this time that I read Effective C++ by Scott Meyers, which I highly recommend to any serious C++ developer. It definitely gave me a more in-depth knowledge of C++. One thing that it also introduced me to was the idea that a class's public interface should be non-overridable by its subclasses and that the functions that a class would override would be protected. I quickly implemented that because I saw the value in pushing error checking higher up the inheritance tree. I don't have it drawn, but a ContainerBase was born out of this design, and it quickly had its own error checking functionality so that by the time a function got down to LinkedList or ArrayList I knew that I had a valid state. It was pretty nice. It led me to discover the dreaded inheritance diamond that gave life to virtual inheritance in C++, but I feel as though I understand that topic now, and I'm once again a happy camper. Then, I began to have some more issues.
I try to test my code with my testing framework that I talked about earlier as much as I can. One caveat with my testing strategies is that I loathe friend classes. I think they're pretty much the devil of computer programming, at least in C++. I don't know if it's my engineering background, but I don't like exceptions to rules, and a friend class is just that: an exception. However, sometimes when you're testing, you really would like to test the internals of a class to make sure that they are working properly. What is a developer to do? One option is to reveal those internals as public functions so that the test code can access and test them, but doesn't that kind of defeat the purpose of making them "internal functions"? I don't want to reveal them on purpose! What's a programmer to do? Enter my latest design strategy:
I call it the crazy-big-and-complicated-architecture-that-is-so-very-nice-but-takes-forever-to-implement data structure pattern! As you can see, the number of classes that I had at the beginning has doubled to get to this structure, but now I can reveal the ArrayList classes to the programmer who will use QubLib and then I can reveal ArrayListImpl to my test cases. ArrayList essentialy defers to ArrayListImpl for everything and it maintains a clean API for a developer to work with. I actually also ended up creating a ContainerBase that functions like ListBase, except on the IContainer level. It's complex, but I think it's worth it. This design also lets me add another class that I cleverly call "List" on the same level as ArrayList and LinkedList. Its sole job is to defer to the fastest default IList data structure in my library. That means that I can swap implementations under the hood, and no one will have to change their code. They'll see an improvement in their library performance just because I changed the backend. I love programming. I was once told that every computer science problem can be solved by just adding another layer of indirection, and boy does that seem to be the case here.
Anyways, that's what I've been working on now. I'm planning on taking this architecture to my smart pointers next and getting a really clean interface there too, and then onto my strings. Lots of work, but I think it'll really pay off once I get this project moving more.
That's all for today. I'll be back next week (hopefully). Keep coding!
Data Structures.
It seems as though I'm destined to work on these forever just because I've changed my design for them so many times, but I think I've finally hit on an architecture that pays off dividends. So, how have I designed them this time around? Let me explain with pictures!
So here's my original data structure design for a LinkedList. It's pretty simple, but that's what I miss about it. I really like the idea of manipulating objects by an explicitly declared interface, and this takes that interface to two different levels: IContainer and IList. I specifically split my interfaces this way because it allows me to share test cases across my data structures. I can talk about that another time, but my test framework works on interfaces, so I can use one set of IContainer test cases on my LinkedList, ArrayList, BinarySearchTree, and anything else that implements the IContainer interface. It's pretty cool and it's saved me a lot of time. Anyways, this was the original design. However, the more I worked with LinkedList and ArrayList, I realized that they shared a bit of code, especially things like checking bounds on the list. So, I decided that they should both inherit from an abstract base class that would implement that functionality and they could use the inherited version.
Tada! ListBase (and several other DataStructureType-Base classes) were born. It was right about this time that I read Effective C++ by Scott Meyers, which I highly recommend to any serious C++ developer. It definitely gave me a more in-depth knowledge of C++. One thing that it also introduced me to was the idea that a class's public interface should be non-overridable by its subclasses and that the functions that a class would override would be protected. I quickly implemented that because I saw the value in pushing error checking higher up the inheritance tree. I don't have it drawn, but a ContainerBase was born out of this design, and it quickly had its own error checking functionality so that by the time a function got down to LinkedList or ArrayList I knew that I had a valid state. It was pretty nice. It led me to discover the dreaded inheritance diamond that gave life to virtual inheritance in C++, but I feel as though I understand that topic now, and I'm once again a happy camper. Then, I began to have some more issues.
I try to test my code with my testing framework that I talked about earlier as much as I can. One caveat with my testing strategies is that I loathe friend classes. I think they're pretty much the devil of computer programming, at least in C++. I don't know if it's my engineering background, but I don't like exceptions to rules, and a friend class is just that: an exception. However, sometimes when you're testing, you really would like to test the internals of a class to make sure that they are working properly. What is a developer to do? One option is to reveal those internals as public functions so that the test code can access and test them, but doesn't that kind of defeat the purpose of making them "internal functions"? I don't want to reveal them on purpose! What's a programmer to do? Enter my latest design strategy:
I call it the crazy-big-and-complicated-architecture-that-is-so-very-nice-but-takes-forever-to-implement data structure pattern! As you can see, the number of classes that I had at the beginning has doubled to get to this structure, but now I can reveal the ArrayList classes to the programmer who will use QubLib and then I can reveal ArrayListImpl to my test cases. ArrayList essentialy defers to ArrayListImpl for everything and it maintains a clean API for a developer to work with. I actually also ended up creating a ContainerBase that functions like ListBase, except on the IContainer level. It's complex, but I think it's worth it. This design also lets me add another class that I cleverly call "List" on the same level as ArrayList and LinkedList. Its sole job is to defer to the fastest default IList data structure in my library. That means that I can swap implementations under the hood, and no one will have to change their code. They'll see an improvement in their library performance just because I changed the backend. I love programming. I was once told that every computer science problem can be solved by just adding another layer of indirection, and boy does that seem to be the case here.
Anyways, that's what I've been working on now. I'm planning on taking this architecture to my smart pointers next and getting a really clean interface there too, and then onto my strings. Lots of work, but I think it'll really pay off once I get this project moving more.
That's all for today. I'll be back next week (hopefully). Keep coding!
Saturday, February 12, 2011
The latest with QubLib
Seriously? Months since my last post? Wow, I must be in school or something. Anyways, despite my lack of blog post updates on my cross-platform library, I have not lacked on working on the library itself. Allow me to explain a few of the things that I've been doing in the past few months:
1. Memory Management - This has been a huge undertaking for me. I recently read a book named Effective C++ by Scott Meyers, which I highly recommend to anyone who is interested in C++ development, which talked about overloading the new and delete operators. It was an interesting idea to me, especially when I put it into the context of a smart pointer that would be able to determine if it was pointing to something that was on the stack or not. With a little fancy overloading and some nifty design, I've successfully created a HeapManager that when given any address, it will tell you whether something is one the heap or not. Cool huh? But the fun doesn't stop there! I decided to create an AllocationManager singleton object that wraps the whole memory management process, so anytime you call new or delete, your code will end up running through the AllocationManager. What's in there? Well, for starters there is an IAllocator object that does two things: AllocateMemory() and DeallocateMemory(). The standard one (called StdAllocator) just uses malloc() and free() like the normal un-overriden new and delete operators do, but with a quick call to AllocationManager::SetAllocator() you can implement your own custom allocator and have your program using it in no time. I have future plans for allowing a user to use different allocators for different types, but I haven't quite gotten around to that. It's a possibility though.
2. Design Patterns - After working with a bunch of singleton objects I finally got sick of rewriting the same code over and over again. I refactored out my singleton code into a single class called Singleton which can be inherited to provide default Singleton functionality to any class. Thank goodness for code reuse!
3. IO - I've started working on file and console input and output. It's a little bit of a pain, but it was coming along smoothly until I wanted a FileReader to be able to point to a FilePtrStream that was on the stack in one function and then one that was on the heap in another function. That's when I went off on the HeapManager trip. Hopefully I can get back to IO soon and put it all behind me.
4. UDP - Before my last post I had gotten TCP operations working for networking, but I hadn't quite figured out UDP. I've gotten it to work. As I was working on the before mentioned IO, I realized that I need to cut a lot of my network code and realize that Network sockets are really just fancy streams that a simple Reader or Writer object could interact with. So, my networking stuff will change again, but I'm hoping that this time it will look really pretty.
5. ThreadSafe - Have you ever been working with a data structure and had that sudden thought, "Uh oh... Is this thing thread safe?" Well, let me put your mind to rest! In QubLib, it's not! None of my data structures are thread safe by default. However, lets say that you created an IList in the following way:
In order to make this newly formed LinkedList thread safe, all you have to do is wrap it in a ThreadSafe function, like so:
And tadaa! It's thread safe now. Some of you may be asking why I don't just make it all thread safe by default? The answer is that I want to give people options. There may be someone who really wants to crank out as much speed as possible, and they just can't afford to have those mutex locks in the code. My first question would be why are they using this library then, but that aside, I still want to provide the options.
I'm sure there are some other little things that I'm missing, but those are the big ones that I've been chugging away on. Right now I'm working on converting my data structures to use a NVI (Non-virtual Interface) design so that all of my public interface error checking can occur in the interface level of the data structure and the rest of the implementation can work off of design by contract principles. There's a lot of future plans I hope to do with this (contractual static flow analysis?), but that's a long way off. For right now, it just helps me fix bugs.
Anyways, that's all for now. Keep coding!
1. Memory Management - This has been a huge undertaking for me. I recently read a book named Effective C++ by Scott Meyers, which I highly recommend to anyone who is interested in C++ development, which talked about overloading the new and delete operators. It was an interesting idea to me, especially when I put it into the context of a smart pointer that would be able to determine if it was pointing to something that was on the stack or not. With a little fancy overloading and some nifty design, I've successfully created a HeapManager that when given any address, it will tell you whether something is one the heap or not. Cool huh? But the fun doesn't stop there! I decided to create an AllocationManager singleton object that wraps the whole memory management process, so anytime you call new or delete, your code will end up running through the AllocationManager. What's in there? Well, for starters there is an IAllocator object that does two things: AllocateMemory() and DeallocateMemory(). The standard one (called StdAllocator) just uses malloc() and free() like the normal un-overriden new and delete operators do, but with a quick call to AllocationManager::SetAllocator() you can implement your own custom allocator and have your program using it in no time. I have future plans for allowing a user to use different allocators for different types, but I haven't quite gotten around to that. It's a possibility though.
2. Design Patterns - After working with a bunch of singleton objects I finally got sick of rewriting the same code over and over again. I refactored out my singleton code into a single class called Singleton which can be inherited to provide default Singleton functionality to any class. Thank goodness for code reuse!
3. IO - I've started working on file and console input and output. It's a little bit of a pain, but it was coming along smoothly until I wanted a FileReader to be able to point to a FilePtrStream that was on the stack in one function and then one that was on the heap in another function. That's when I went off on the HeapManager trip. Hopefully I can get back to IO soon and put it all behind me.
4. UDP - Before my last post I had gotten TCP operations working for networking, but I hadn't quite figured out UDP. I've gotten it to work. As I was working on the before mentioned IO, I realized that I need to cut a lot of my network code and realize that Network sockets are really just fancy streams that a simple Reader or Writer object could interact with. So, my networking stuff will change again, but I'm hoping that this time it will look really pretty.
5. ThreadSafe - Have you ever been working with a data structure and had that sudden thought, "Uh oh... Is this thing thread safe?" Well, let me put your mind to rest! In QubLib, it's not! None of my data structures are thread safe by default. However, lets say that you created an IList in the following way:
IList<int>* intList = new LinkedList<int>();
In order to make this newly formed LinkedList thread safe, all you have to do is wrap it in a ThreadSafe function, like so:
IList<int>* intList = ThreadSafe<int>(new LinkedList<int>());
And tadaa! It's thread safe now. Some of you may be asking why I don't just make it all thread safe by default? The answer is that I want to give people options. There may be someone who really wants to crank out as much speed as possible, and they just can't afford to have those mutex locks in the code. My first question would be why are they using this library then, but that aside, I still want to provide the options.
I'm sure there are some other little things that I'm missing, but those are the big ones that I've been chugging away on. Right now I'm working on converting my data structures to use a NVI (Non-virtual Interface) design so that all of my public interface error checking can occur in the interface level of the data structure and the rest of the implementation can work off of design by contract principles. There's a lot of future plans I hope to do with this (contractual static flow analysis?), but that's a long way off. For right now, it just helps me fix bugs.
Anyways, that's all for now. Keep coding!
Subscribe to:
Posts (Atom)