in C++, Tools

Are We There Yet? SlickEdit’s C++ Refactoring

Jumbo shrimp. Instant classic. Military intelligence. C++ refactoring browser. Spot the pattern yet? Up until recently, there have been more sightings of Nessy and Bigfoot than of working C++ refactoring browsers. After months of using refactoring intensively in C++, my fingers are screaming for mercy and threatening me with repetitive stress syndrome. Fortunately, things seem to be changing a bit. I recently learned of SlickEdit‘s support for C++ refactoring, so I couldn’t resist taking it for a test drive.

C++ refactoring is hard. There’s no doubt about that. Just parsing C++ is hard, as I quickly found out when I wrote some scripts a couple of years ago to analyze the structure of a C++ program. Even with the help of Doxygen pre-processing, my analysis was full of holes and exceptions. Writing a robust program to correctly interpret and modify the structure of a C++ program is a nightmare come true.

Frankly, I had pretty much given up all hope of every having good automated refactoring tools for C++. My best hope was that new language designers keep refactoring in mind as a primary goal of their languages, so at least we’ll have those tools in whatever language we’re programming with in ten years.

So along comes SlickEdit version 10 with great promises of C++ refactoring. Surprisingly enough, that’s just one small bullet point completely hidden by a bunch of bullet-point items of no great interest or uniqueness (smart line selections? keyboard emulation? what year is this, 1992?). As a matter of fact, refactoring isn’t even mentioned in SlickEdit’s “cool features” web page! That’s not an auspicious beginning.

Fortunately, things get better. The installation for the Linux demo version of SlickEdit v10 worked without a hitch. The refactoring actions feature prominently in the Tools menu and the right-click context menu. I loaded a small project I was working on (just a handful of classes-nothing big) and started applying them one at the time.

Rename

nessy This is the big one. If I can only have one refactoring, I’ll take this one. Being able to rename variables, functions, and classes takes care of 75% of the refactorings I do. A tool that could reliably do that would be a major productivity boost (reliably being the key word there).

How does SlickEdit’s rename work? Member variables were a dream to rename. But frankly, I can usually do that with “replace in file” and doing one edit in the header file, so that’s not very challenging. Still, good start.

Trying to rename member functions was not quite as successful. Sometimes it would work like a charm, and rename the function in the cpp file, the header file, and everywhere else it was called from. Sometimes however, it would look at my code and proclaim that there was nothing to rename. Nothing? I had just right clicked on top of the function to rename! Some other times it would rename the function in a couple of classes and totally miss where it was being used.

After some experimentation, it turns out that “Quick Rename” (which supposedly is not nearly as robust) worked time and time again without a single problem. That’s good I guess, but it left me with a very uneasy feeling.

Thumbs up for the user interface for refactoring too. As soon as you enter your refactoring and your parameters, you are presented with a list of the files that will be modified and a diff of each of them, so you can cancel the operation if something went horribly wrong. It certainly makes you feel a lot more in control.

Extract method

This is a fairly common refactoring. I actually thought it would be fairly complicated, but in every test I did, SlickEdit handled it admirably. The tricky part is to figure out what arguments the new function needs to take, and every time it figured it out correctly. You even get an option to name the new function and tweak the arguments it takes before applying the refactoring. Again, great user interface.

My only quibble with this refactoring is that the new methods are all made public. Uh? Would it have been so difficult to give us an option? If anything, I have just moved code from a member function to another member function in that class. By default it should be private, not public. Oh well, I just have to move one line. But it’s a bit annoying to have to tweak the header file by hand.

Modify parameter list

Another big one. Sometimes it’s quite painful to add a new parameter to a function that is called from dozens or hundreds of tests. So much so that it really discourages us from doing the “right” thing and is tempting to create a new function instead. Sometimes, after the second time you need to add a new parameter, we just create a struct with the parameters that the function takes and save some pain in the future. Not a particularly elegant solution, so this refactoring tool would come in very handy.

Unfortunately this one completely failed for me. Most of the time, trying to add a new parameter to a function would just come back and tell me there’s nothing to do. Same thing with rearranging the order of parameters. At best, once I got it to actually add the new parameter, it only changed the function and the header file, not the calling code. Doh!

Misc inheritance chain refactorings

SlickEdit has a set of refactorings intended to do operations in the inheritance chain: push down, pull up, extract super class, etc. I don’t work with big inheritance chains these days, so they’re not very useful to me and I didn’t test them at all. Actually, now that I think about it, the only inheritance I use are interfaces and mock objects. Then again, I suspect this would be extremely handy for manipulating legacy code (like some currently popular game engines).

Extract class

This seems like a really large refactoring. Extracting a whole class out of a set of code? Rename didn’t work consistently, and modify parameter list didn’t work at all. I certainly wouldn’t want to try something of this magnitude. Let’s get the other ones working before we jump on this one.

Convert local to field

This one seems marginally useful, and it’s not something I do in a regular basis. All it does is to prepend an m_ to the variable and add it to the header file. What if you don’t prefix your member variables with m_? Is there a way to turn that off? Also, the new member variable wasn’t added to the constructor(s) initialization list.

Move method

At first I thought that it was a totally pointless refactoring since it’s describing as moving a method from one class to another (I can’t remember when was the last time I did that). But it turns out it can be quite useful to move a non-member function into a class. That’s something I do sometimes when a helper function in an anonymous namespace starts needing more and more state from the object that uses it, and eventually it becomes clear that it wants to be a member function.

Move method will make it a static member function, and then you can follow that up with Convert static method to instance method and voila.

Encapsulate field

I’m sure some people will rave about this one, but I’m not too thrilled about it. It creates set/get methods of a member variable. Frankly, I don’t like set/get methods. I think that, most of the time, they’re a smell of bad design. I’m not sure I want to make it any easier for people to create them, so I would be happier without this refactoring tool at all. Besides, what’s with making them inline by default? At least give us an option to put them in the cpp file.

Replace literal with declared constant

This one takes a literal (magic number/string) and replaces with with a constant or a define. Now we’re really reaching the bottom of the barrel. Is it really faster to navigate a three-level menu to select this than to do it by hand? It’s good that you get an option to create the new constant as const, static const, or an evil #define, but it doesn’t place it in an anonymous namespace, even if one already exists in that compilation unit.

Create standard methods

This one will add a constructor, destructor, copy constructor, and assignment operator to the class. At first I thought it wasn’t a big deal. I already have macros that do that for new classes. Then I realized you can do this with an existing class, and it will take care of assigning all member variables. That’s pretty cool, but of limited practical use. I haven’t had to go back to many existing classes and add assignment operators. Still, I could see how it could be useful.

yeti All in all, SlickEdit managed to impress me because I wasn’t really expecting functional C++ refactorings. On the other hand, it is clearly far from being robust enough to be relied upon for production work. Maybe the next version? For me, they just need to fix the rename and I’m almost there.

One cool aspect of SlickEdit is its powerful scripting language, where it has access to all the commands you can do from the menu. That means that I can write a script that, as part of what it does, kicks off some refactoring step. That’s extremely useful to adapt the refactorings to my own environment. One thing I didn’t see is whether the script language has access to the logical structure of the C++ program or whether it’s just limited to calling the refactoring function. It would be great to have more control and be able to create custom refactorings.

SlickEdit is a bit puzzling because it parses all the header files during each refactoring. And by all, I mean all. Not just the ones in your projects, but all the standard include files that you might have included. I don’t really understand why it does that. I’m clearly not going to change standard header files, so I think that limiting a refactoring only to files that are presently loaded in the project would be good. With my toy projects with a handful of classes, the refactorings were very fast (a second or two), but I fear how it might scale to a large project. Next time they have an improved version I’ll have to load it up with a large codebase and see how it fares.

It seems as if refactoring tools for C++ are just around the corner, but still tantalizingly out of reach. At this point I’m not ready to switch to SlickEdit based on the strength of its refactoring tools alone, although I’m still going to take a closer look at it. Right now I would prefer a standalone command-line refactoring tool that could be easily integrated in any environment. Anybody knows of any other decent C++ refactoring tools?

11 Comments

  1. Hey Noel, I notice that SlickEdit also have a beta going for Visual Studio. Since our company primarily mainly use Visual Studio (and the other team mixes into the mix CodeWarrior) having the refactoring tools available inside Visual Studio looks useful.

    I will have to go through and test the functionality of the refactoring tools inside Visual Studio.

  2. It’s not there yet. I ran the PC demo, and the thing crashes as if it would go out of style tomorrow. What’s worse, I send the company a list of detailed bug reports (+repro cases) several pages long, and never heard back from them – not my idea of customer service. Yes, it was only the demo – still, at least a “Yup. Got it” would’ve been appropriate.

    Add to that the fact that I have a Mac laptop while I do most of my “work” work at a PC, and they want me to buy dual licenses for that setup. I’m not shelling out $600 for a buggy editor.

  3. Cool, I hadn’t seen the Visual Studio plugin beta. I’ll give it a try over the next few days with a much larger codebase at work and see how it goes.

  4. Thats very cool. thanks, I will have to check this refactoring engine out.

    I was trying to build a refactoring/reflection/metaobjects system for C++ when I gave up on C++ and moved on to D. Ive never even heard of a language more difficult to parse than c++. You can parse D code with your eyes closed so building these tools are cake. No required forward references is more liberating than you can imagine and the compile times are 1/50th of c++. I think D is the future for game development.

  5. I gave the demo a whirl for our Spider-Man codebase – it can supposedly handle a .NET project, but we use makefiles, and it would have taken me a lot of work to show SlickEdit where all the files were, and I’m barely even a programmer anymore, so I gave up. But I’m surprised by Robert’s claims about bad customer support: they kept asking me what my problems were, assigned a tech guy to me, etc.

  6. Interesting the link for Ref++. I will definitely give it a try. It’s unfortunate that it’s only a Visual Studio plug-in and not a standalone tool with an option to integrate in VS.

  7. Jamie: I have no idea. I used to use SlickEdit, and they had better support back then. Maybe because I was only a demo user?

    Anyways – the dual license for PC/Mac was the real kicker for me. I could’ve lived with the occasional crash(can’t be worse than VisualStudio….), but $600 out of my own pocket is beyond what I’m willing to spend on a tool

  8. I agree with you it sucks to get charged twice for 2 platforms but in general price should not be that big of an issue if it makes you more productive. Especially if you are a programmer your editor is the single piece of software you use the most. Get a good one. $300 is about $1.20 a day (assuming 250 days of work a year). I’ve been with slickedit since version 1 and I know it’s certainly been worth for me.

    They’ve had many things relatively early (first?) for example tags with pop up help since 1998? Of course some of that stuff is now built into VC++ but back then, especially when joining a project already in progress with a large unfamiliar code base it makes it extremely easy to jump around the code quickly. Pop into a function or structure def, follow it’s fields or variables, pop back out to where you were.

    I’ve never had any real issues with support over the 12 years I’ve been using it. I’m sorry to hear you had trouble.

    Of course programmers and editors are like religion so use whatever makes you happy and productive. This is probably a sad reason but I originally choose slickedit because it nearly perfectly emulated Brief. Something other editors claimed but all of them failed at. One thing I like about it (I’m sure some other editors can do this) is that you can configure it to never need a mouse. I can jump between windows, split them, join them etc. Open files, search, etc all without every touching a mouse and without having to tab around dialogs.

  9. Thanks for the fair and balanced review. I’m the Director of Software Development at SlickEdit, and thought I’d share a little about this feature.

    As you noted, C++ refactoring is an extremely difficult problem. You have to contend with macros, templates, and the liberties the compiler vendors have taken with the language spec. We designed our approach around a strong parsing and type analysis engine, and consequently it is very sensitive to the code that it is processing.

    When I wrote our Cool Features document, I consciously left off the C++ Refactoring because it doesn’t work for all codebases and still struggles with some situations. If it worked as well as the features currently listed, it would definitely have made the list.

    We are continuing to enhance the engine and work on additional “quick” refactorings that are based on our tagging engine. As you pointed out in the review, we consider these to be less “robust”. That doesn’t mean that they are more fragile, but that they may make changes that are not strictly accurate, where the regular refactorings will do more analysis to weed out these cases.

    Unfortunately, we don’t provide access to the underlying representation of the code, as you have asked. Likewise, it would be very difficult to provide a means to allow users to write their own refactorings. But that’s a really great idea!

    Feel free to contact me with ideas and questions. We’re a company of hard-core C++ programmers and we love to talk about coding!

Comments are closed.