I just finished writing a string class that includes functions for generating random string (why I don't know, as I have never really needed random strings before). Basically the user can pass the function an enum citing what kind of string that'd like (i.e. AllUpperCase or AllNumeric). There is also an option for generating random hexadecimal, octal, and binary string representations. As it is, hex always begins with "0x", and binary with "0b", but I'm not sure what I should do for octals. I know the C/C++ compiler treats any number literal beginning with a 0 as an octal, but I always thought that was too confusing, as it is also a valid looking decimal number. Wikipedia suggest a solution to this is to use "0o" or "0q" in front of the number to distinguish it as an octal, and I was just wondering what other people's opinions were.
Well if the string class is C++ then I would adopt its convention..
I had that thought as well, but then my overwhelming hatred of the C++ syntax for it kicked in.
I had that thought as well, but then my overwhelming hatred of the C++ syntax for it kicked in.
Then why are you coding in C++?
I like C++. I just don't like the syntax for octals.
what are you trying to do with the octals?
Why don't you seperate the randomString(HexOrDecOrOctal) function from the class, and
make it a regular function. Like so,
MyString genRandomString(int radix){
validate(radix);
//...
}
No need for enums or IDs, just pass in the base, and the function returns something
that has to do with that base.
>I was just wondering what other people's opinions were.
I wouldn't bother with any kind of marker in the string. The caller clearly knows what radix they want to generate, so generate the string and let them add a prefix (or suffix) if they so desire.
@firstPerson: The way it's set up right now, the enum can also denote things not related to integers of different radices, such as strings of all capital letters. And while I certainly could have a function for that kind of thing, it'd introduce new problems, such as what to do when an invalid base was passed. I guess just return an empty string?
@Narue: you bring up a very good point. I think I might go with that suggestion.
@firstPerson: The way it's set up right now, the enum can also denote things not related to integers of different radices, such as strings of all capital letters. And while I certainly could have a function for that kind of thing, it'd introduce new problems, such as what to do when an invalid base was passed. I guess just return an empty string?
@Narue: you bring up a very good point. I think I might go with that suggestion.
Your design seems to be very coupled. Using enums like that is not a good idea. That's
not what enums were made for. You would be better of doing something like this :
MyString genRandomRadix(int radix) throw InvalidRadixException(){
//blah blah blah
}
MyString toUpperCase(const MyString& str){
//more blah blha blah
}
That way you can use it like so :
MyString id = toUpperCase( genRandomRadix(BASE_16) ); //ex: 0xFFF12 instead of 0xff12
//versus what you probably do :
MyString id2;
id2.genRadomRadix( MyString::HEX_BASE ).toUpperCase();
The first one is much clearer and more usable in my opinion.
@firstPerson: I really don't see much of a difference in our two designs, other than yours being used to generate numbers of any radix. And I don't like the fact that your design doesn't encapsulate string functions. Honestly I think the second example you give is much clearer as it can be read from left to right, instead of inside to outside, and other than the variable radix thing, it's equally effective. What's more, I'm not entirely sure what valid radixes (is that the plural of radix?) I would enforce.
The difference comes in for later maintenance. If you want to add more radix to you would have to add more enums. And if you want to disable the use of a radix you need to remove enums, and other codes. The way I showed you might makes it easier to handle change. As always, you should design for change.
You are adding things to your string class that does not need to be in there. Why
should a string class have a function to generate a random string of a certain base.
That should be a regular function. Once again, a class should hold the minimal amount
of data that it needs.
A valid radix is usually from base 2-26, but you can design for almost any valid radix if you want. Its your decision.
The bottom point is, whether you see it or not. The first design is more flexible because it does not have the string class heavily depend on other external things, than what it needs to depend on. Its easier to maintain and change.
As always, there are many ways to design a class to solve a problem. A good way to design such a class, is to think in the future, and see what can be abstracted.
So that the design becomes more flexible, robust, and reusable.
I understand the benefits of not using enums for denoting the radix to use. And as for "Why should a string class have a function to generate a random string of a certain base." I'm going to go with because the function has to do with strings. And not just strings in general, it uses my string class. By putting that function there it makes it clearer that the function is meant for handling my string class, not std::string or C strings. By having it a global function, it loses that association. For that matter, if I saw genRandomRadix() I wouldn't immediately think it had anything to do with strings, let alone my string class. I'll admit using enums to denote what kind of string to generate is limiting, and harder to add functionality to, but I see no reason that the function itself should be separated from the string class. And bear in mind, this function is static. It could be used in the same ways as a global version of the function, but the association with my string class is made clear.
>>Why should a string class have a function to generate a random string of a certain base." I'm going to go with because the function has to do with strings. And not just strings in general, it uses my string class. By putting that function there it makes it clearer that the function is meant for handling my string class, not std::string or C strings. By having it a global function, it loses that association. For that matter, if I saw genRandomRadix() I wouldn't immediately think it had anything to do with strings, let alone my string class.
Then why create your own string class? Just inherit from std::string if you need to.
That way, you can use polymorphism and generateRandomStringInBase(int base), could
work with either.
>>And bear in mind, this function is static. It could be used in the same ways as a global version of the function, but the association with my string class is made clear
If the function is static, then that should be a big clue that, that function needs
to be a standalone function. That it does not need to be coupled with your string class.
>>if I saw genRandomRadix() I wouldn't immediately think it had anything to do with strings, let alone my string class
Yea I admit it wasn't the best name. How about, generateRandomStringInRadix(int radix) ?
And BTW: using your way, you would always have to create a MyString class. Do you think you should have to create a MyString just to use the generateRandomString function?
"Then why create your own string class?" I found std::string didn't support some of the features I'd like it to.
"Just inherit from std::string if you need to. That way, you can use polymorphism" None of std::string's functions are virtual, meaning I can't do polymorphism, and the destructor isn't virtual while it is likely nontrivial. What's more my string class has both a GetStdString and GetCString function, meaning that it can be used for anything requiring a std::string or char array.
"If the function is static, then that should be a big clue that, that function needs
to be a standalone function. That it does not need to be coupled with your string class." Why? You have provided no evidence for any of the suggestions you've given. You've said that I shouldn't use a static class function because it is more easily maintained, but you've given absolutely no examples to back that statement up.
"And BTW: using your way, you would always have to create a MyString class. Do you think you should have to create a MyString just to use the generateRandomString function?" Yes. I think that a function that generates a string should use an instance of a string. Now, like I said the function is static, and it returns a string. Meaning you could easily do "cout << MyString::RandomRadix(someRadix)". But to store the random string in something other than a temporary you would need to create an instance of it. Your solution is no different.
>> I found std::string didn't support some of the features I'd like it to.
Then inherit and add those features.
>>Why? You have provided no evidence for any of the suggestions you've given. You've said that I shouldn't use a static class function because it is more easily maintained, but you've given absolutely no examples to back that statement up.
Its hard to come up with a concrete example, but generally speaking it is easier to
maintain and change code that has loose coupling. Meaning that, it is easier to add
new features to a program that does not depend heavily on other things. As an example, consider this trivial problem: Design a program where a button controls a lightbulb, turning it on and off every other time the button is pushed. So its basically a lamp. A simple design for this might be like so :
class Lamp{
Button button;
LightBulb lightBulb;
public:
//button turns on/off every push
void off(){ button.pushdown();}
void on(){ button.pushdown();}
//...
};
This seems like an OK design, but notice that its highly coupled. Does every lamp
have to have a button to turn lightbulb on and off? Can we use some string to pull,
instead of the button, how about some type of voice recognition to turn light bulb on/off? So you see, using this design we would have to create many separate Lamp classes and whatnot. But if our design were better, meaning that lamp was not tied down to having the button control the bulb, then don't you think it would be easier to add new features? Thus we need a good, a better design. For that see this paper.
The problem above has the same problem as your code, both have more coupling that needed. Thus it considered good practice to reduce
the coupling between classes and objects.
Your program is trivial that it might not make a big difference whether your method is static or a regular function. But in a larger program it will. All I am trying to break you out of some bad habits, but do as you please. It's no lost for me if you stick to your habits.
See? That was actually a very good way of supporting your point. My one criticism of that article is it presumes that only object-oriented design may be used to solve the problem, whereas many languages feature multiple paradigms. In regards the the problem, I think a simple solution would be the use of an interface from which all "Button" types must derive from, and a templated Lamp class that takes a implementation of the prior interface. That said it does highlight a legitimate problem with object oriented designs.
However, it still doesn't quite sell me on separating the random function from the string class for a few reasons. The first is organization. One thing I hate about the C standard library is that I never know what features there are, nor where they are declared. And while the solution is as simple as looking up documentation online, this becomes tedious when extensive use of the library is needed. Moreover, even when I know about a feature, I often forget the exact name of a function. With the advent of Intellisense and other such features, finding a particular function inside a class or namespace becomes trivial. Thus I believe it's better to organize code into classes and namespaces that denote the most logical associations. In this case, functions that I write that work with strings ought to belong in the string class, as that is where one would expect to find them. You could think of it like the folder structure of a computer. While I could place a great deal of files on the desktop, this quickly clutters the desktop, and causes issues if I want two files on the desktop with the same name. By organizing files into appropriately named folders, it makes it easier to find things, and lowers the chances of name collisions.
Now, extensibility is also an important feature of a good library. And looking at std::string is a great example of it. All of the C standard library works using character arrays (obviously) as well as a much of the C++ standard library. This makes sense, as C strings are the closest thing C and C++ have to a native string type. std::string seeks to make string handling easier, but it also needs to be able to interface with functions expecting C strings. Thus it has the c_str() function. This allows the user to utilize the strengths of std::string while being relatively backwards compatible as well. My string class essentially wraps std::string, and provides a function for getting a C string, as well as a function for getting a std::string. Thus my string class can be used in conjunction with all foreseeable C++ functions. Moreover, this string class can support polymorphism, which is something that cannot be done using std::string.
Ultimately I agree with you that objects should avoid being linked together in such a way that creates dependency on other objects, but I would like to point ought that the crux of the lamp problem was that two separate objects were being linked together, rather than an object being linked to a set of functions that are associated to it. I don't think the problem here is similar enough to the problem created by the lamp example to warrant changes.
Ok then maybe scott meyers can convince you on why to prefer non-member function rather than member functions.
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.