|
nuthole.com
"even better than a poke in the eye with a sharp stick"
contact: subscribe: follow me on twitter: 2007 10 |
|
|
2007 10 29 - Mon Defective C++posted by jack at 06:12 CET in / compute / programming
At my previous job, I worked as a C++ programmer. The job, which started out primarily working with Objective-C and Objective-C++ to port a Windows application to Mac OS X, became progressively more a C++ job as time went on. I came to learn C++ fairly well, just enough to realize how much I dislike it. I haven't properly been able to summarize just why I dislike C++ so much, but fortunately someone else has: The Defective C++ page, part of the larger "C++ FQA", lays bare a number of problems inherent to the design of the C++ language.
In less than two years of C++ usage, I encountered nearly every one of these problems. Some of them can be dealt with by deciding upon and following "best practices" within the team, which we did (to give credit where credit is due, the more experienced C++ developers I was working with pretty much laid out the best practices for the rest of us to follow), but some of them are just things that you pretty much have to live with, and end up turning the development process into a real straightjacket.
(via Another Day in the Code Mines, which had picked it up from a discussion at Joel On Software; check out the comments in that discussion if you want to see some extremely blindered C++ apologists in action...)
|
| permalink | digg slashdot del.icio.us | 0 comments (why don't you write the first?) |
Yesterday I installed Apple's new Logic Studio. The system I was installing it on was already crowded, with the first four Jam Packs among other things, and I only had about 11 gigs free. To top it off, the Jam Packs weren't properly installed; I had manually copied them from an older machine, but in a slightly non-standard location, without the .pkg files etc, so the Logic Studio installer couldn't see them and wanted to do a fresh install of the new versions included with Logic Studio.
So I figured I'd wipe out the old Jam Packs manually, and let Logic reinstall them, and hope I'd have room for all the parts of Logic I wanted. I stumbled across this thread on Apple's support site, which mentioned that the new versions of the Jam Packs in Logic Studio now have audio saved in compressed (but lossless) format, so they're a bit smaller. Cool!
So, I wiped out my old Jam Packs, and installed most of Logic Studio, including all software and all 5 Jam Packs, but not the new extra audio content (which as far as I can tell seems to be intended mostly for soundtrack use). After installing all that, I ended up with 17 gigs of free space! Installing the new version, with more stuff, saved me 6 gigs! Woot!
| permalink | digg slashdot del.icio.us | 0 comments (why don't you write the first?) |
There seems to be a sort of mystique around the concept of metaprogramming, but in Ruby it's really not mystical at all. It's all about leveraging a few existing methods in smart ways to let you eliminate boring code. This sort of thing is possible in my "native language" of Objective-C, but in Ruby it turns out to be even easier.
Today I'm going to talk about how to use the method_missing method to eliminate repetitive boilerplate code. method_missing is Ruby's fallback strategy for unresolvable method names encountered at runtime. If you try to call a non-existent method on any object, the Ruby runtime will instead call method_missing on that object; The default behavior of this method is to raise an exception, but we can override it to do smart things based on the method name.
The Rails application I'm working on has a role-based permissions scheme, with a ManagementRole model that sits between User and Program models, allowing us to define roles that a User can have relative to a given Program, which in turn constrain what they're allowed to do with the application. These roles are expressed concretely as simple subclasses of ManagementRole, with names like RoleSearchMembers and RoleCreateMemberships. Up until recently, checking whether a User were allowed to perform certain tasks within a Program consisted of calling one of several nearly-identical class methods defined on ManagementRole, such as these:
def self.allow_search_members?(user, program)
return User.is_administrator?(user) ||
self.program_role_exists?(user, program, "RoleSearchMembers")
end
def self.allow_create_memberships?(user, program)
return User.is_administrator?(user) ||
self.program_role_exists?(user, program, "RoleCreateMemberships")
end
# etc
# the program_role_exists? method, not listed here, simply checks
# for the existence of a matching ManagementRole object
As you can see, this is extremely repetitive, and adding new types of permissions means adding new, nearly-identical methods. As of now there are 18 methods like this, and that number will just go up in the future, leading to even more boilerplate code. Where will this madness end?
Providing a simple definition for method_missing in the same class lets me eliminate all those 18 methods, and will automatically deal with any new ManagementRole subclasses. It looks like this:
# instead of a bunch of allow_xxx_xxx_xxx? methods that differ only slightly,
# we just catch all such calls here.
def self.method_missing(method_id, *args)
if match = /allow_([_a-zA-Z]\w*)\?/.match(method_id.to_s) and args.size==2
return User.is_administrator?(args[0]) ||
self.program_role_exists?(args[0], args[1], "Role#{match[1].camelize}")
else
super
end
end
The first thing this does is examine the method and the argument list, to determine if it looks like the kind of method we are trying to replace. If the method name looks anything like "allow_xxx_yyy_zzz?", and if there are exactly 2 arguments, then we'll do something interesting. Otherwise, we'll just call super and let the parent class deal with it.
In the "interesting" case, we simply perform the same checks we did before to determine whether we return true or false, but now we're using indexed values from the args array, and constructing the string naming the MangementRole subclass by using the result of the earlier regexp match. Voila!
Of course, software development being what it is, any time you revisit an old design to try to improve it, the act of improving it can lead you to discover new, further improvements. While writing this, I realized something that would have avoided all this boilerplate code, and the metaprogramming it led to, in the first place: Instead of asking the ManagementRole class explicitly about each of these permissions, I'll be better off writing a single method called allow? in the ManagementRole class, which will do a lookup based on the recipient's class-name, e.g. RoleSearchMembers, RoleCreateMemberships. And then instead of calling ManagementRole.allow_search_members?(u,p) , I will call RoleSearchMembers.allow?(u,p). That will lead to even less code than the metaprogramming version, and less code is always better code. I haven't written it yet, but it'll probably look something like this:
def self.allow?(user, program)
return User.is_administrator?(user) ||
self.find_by_user_and_program(user, program)
end
So this problem, with its metaprogramming-to-the-rescue solution, turned out to be easily solvable with more traditional object-orientation, but that's OK! Hopefully this post will be helpful anyway, for someone else whose problem doesn't have the same easy solution that mine turned out to have...
| permalink | digg slashdot del.icio.us | 0 comments (why don't you write the first?) |
"Re: I Can't Believe I'm Not a Millionaire" by "Jin Ryu"