Creating a more concise C++

I have been thinking about this for quite some time. I have been using C++ off and on for about 8 or 9 years now and one thing that bothers me is that C++ can be quite clunky sometimes. There were time that I had to write 8 or 9 lines of code just to do one simple thing. I have made a small library that I called Concise to help alleviate this problem. I want to put clunky code into this library and make it easier not only to read and use, but to understand in as few words as possible, which is why I named it Concise. this is a solution to a problem that may not be a problem to advanced users of C++ but I think it would be good for beginners and people that have a difficult time learning C++ such as myself. I would like to make this a community project and anyone who is interested to join in. If there is any code you think could be made more concise, you can add it to the library. The goal of this project is to make a library that can be easily used and read but still maintain the flexibility of C++, and to try to create code in just a few lines or less. Some examples Are this:

If i want to display system time, i just type:

Concise::Utilities::Time::ShowSystemTime();

If i want to compare two strings i can type this:

Concise::Compare::String(string1, string2);

To get a random number, instead of typing:

1
2
3
4
5
srand(time(0));

            int r = rand();

            r = rand() % numberSize;


Which isnt a lot of code at all but still. I just type:

cout << Concise::Random::GetRandomNumber(500) << endl;

I'm trying to group things into categories and make them easier to use basically. This will work for the whole std library, like strings, vectors, arrays, etc.

Im not the greatest programmer so the library has basic nontrivial stuff in it but thats why i want to open this up to the community so we can create something that will make C++ easier and faster to use and read. I would like to simplify more complicated stuff, but that would require the help of more advanced users. I have pasted the ConciseLibrary.h code below. If there is enough interest I will put it on github.

MAIN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include "ConciseLibrary.h"

using namespace std;
using namespace Concise;

int main()
{
    vector<string> vect;

    Concise::FileIO::LoadFile("File.txt", vect);

    string text;

    Concise::String::ConvertToString::Vector(vect, text, true, " ");

    string the = "the";
    string rep = "TEST";

    cout << "\n\n" << endl;

    cout << Concise::String::Replace(text, the, rep);

    string i = "e";

    cout << "\n\nThere are: " << Concise::String::Find(text, i) << " Occurences of: " << i << endl;;

    return 0;
}



ConciseLibrary.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#ifndef TMG_H_INCLUDED
#define TMG_H_INCLUDED

#include <sstream>
#include <limits>
#include <ctime>
#include <stdlib.h>
#include <time.h>
#include <fstream>

namespace Concise
{
    namespace String
    {
        int Find(std::string stringToSearch, const std::string wordToFind)
        {
            unsigned int wordAppearance = 0;

            for(unsigned int i = stringToSearch.find(wordToFind, 0); i != std::string::npos; i = stringToSearch.find(wordToFind, i))
            {
                wordAppearance++;
                i++; //Move past the last discovered instance to avoid finding same string
            }
            return wordAppearance;
        }

        std::string Replace(std::string stringToSearch2, std::string& wordToFind2, std::string& replaceWith)
        {
            for(unsigned int i = stringToSearch2.find(wordToFind2, 0); i != std::string::npos; i = stringToSearch2.find(wordToFind2, i))
            {
                stringToSearch2.replace(i, wordToFind2.length(), replaceWith);
                i++; //Move past the last discovered instance to avoid finding same string
            }
            return stringToSearch2;
        }

        namespace ConvertToString
        {
            std::string Int(int integer)
            {
                std::stringstream convert;
                convert << integer;

                std::string str = convert.str();

                return str;
            }

            std::string Float(float floatingPoint)
            {
                std::stringstream convert;
                convert << floatingPoint;

                std::string str = convert.str();

                return str;
            }

            std::string Double(double doubleNumber)
            {
                std::stringstream convert;
                convert << doubleNumber;

                std::string str = convert.str();

                return str;
            }

            std::string Char(char character)
            {
                std::stringstream convert;
                convert << character;

                std::string str = convert.str();

                return str;
            }

            void Vector(std::vector<std::string> convertVector, std::string& stringToUse, bool appendChar, std::string charToAppend)
            {
                if(appendChar == true)
                {
                    for(unsigned int i = 0; i < convertVector.size(); i++)
                    {
                        stringToUse.append(convertVector[i].append(charToAppend));
                    }
                }
                else
                {
                    for(unsigned int i = 0; i < convertVector.size(); i++)
                    {
                        stringToUse.append(convertVector[i]);
                    }
                }
            }
        }
    }

    namespace Random
    {
        int GetRandomNumber(int numberSize)
        {
            srand(time(0));

            int r = rand();

            r = rand() % numberSize;

            return r;
        }
    }

    namespace FileIO
    {
        void SaveFile(std::string fileName, std::vector<std::string>& output)
        {
            std::ofstream save;

            save.open(fileName.c_str());

            for(unsigned int i = 0; i < output.size(); i++)
            {
                save << output[i];
            }
            save.close();
        }

        std::vector<std::string> LoadFile(std::string fileName, std::vector<std::string>& input)
        {
            std::ifstream load;

            load.open(fileName.c_str());

            std::string str;

            while(load >> str)
            {
                input.push_back(str);
            }
            load.close();

            return input;
        }
    }

    namespace Utilities
    {
        namespace ClearInputBuffer
        {
            void ClearMaxInput(char delimiter)
            {
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), delimiter);
            }

            void ClearInputLimit(int ignoreLimit, char delimiter)
            {
                std::cin.ignore(ignoreLimit, delimiter);
            }
        }

        namespace Time
        {
            void ShowSystemTime()
            {
                // current date/time based on current system
                time_t now = time(0);

                // convert to string
                char* dateAndTime = ctime(&now);

                std::cout << dateAndTime;
            }
        }
    }
}

#endif // TMG_H_INCLUDED 
Last edited on
I don't want to be mean, but why, dude, why? Namespace "compare" is an abomination. a == b already evaluates to a boolean. Those are truisms and bloated code, what's easier to type : bool isEqual = (str1 == str2); or bool isEqual = Concise::Compare::String(str1, str2);? std::to_string, already exists in the ISO standard, also you could have used a template for it, or at least override the same function. Your RNG function seeds rand every call, which may prove unnecessary, also you call rand() 2 times, 1st time it won't be of any use because you overwrite r anyway.

If you want concise code, why use this:
1
2
3
int r = rand(); // do you really need a new variable?
r = rand() % numberSize; //why? you already generated a random number
return r;


instead of

return rand() % numberSize;
Last edited on
There may be some valuable ideas there.

However, just glancing at it briefly, I don't see how writing this:
 
    Concise::Compare::String(string1, string2);


is better than typing
 
    (string1 == string2);


I'd suggest that the entire block namespace Compare { } (about 60 lines of code) is redundant, it doesn't seem to do anything which isn't already available in a more concise form in the basic C++ language.
Last edited on
It's verbose and nested into 2 namespaces, also there is already a std::equal_to.

Also this:
1
2
3
4
5
6
7
8
if(char1 == char2)
{
    return true;
}
if(char1 != char2)
{
    return false;
 }


Which could as well be replaced with
return char1 == char2;

And the whole compare namespace could be replaced with:

1
2
3
4
5
template<typename T>
bool inline isEqual(T const & a, T const & b)
{
     return a == b;
}
Last edited on
I did that mainly for organization, sure, typing if(a == b) is easier but its just for organization, also this is just an example. Like i said, im not a great programmer which is why I would like someone with a much higher knowledge to think of some code that could be made simpler and we could build something with more value that the current library i have come up with. The code in my first post is just proof of concept, some or all of it could not be used int he actual library. I would actually like to get some string stuff in there, like make functions that make it easier to search through text and find and replace strings, count characters, and do pretty much everything else in the string STL and make it more concise and easy. I would also like to create one for file handling and some other things as well. Also for a beginner, if they want to do string stuff they could just type:

Concise::Compare:: (I may change this to Concise::String::Compare) and intellisense will show them a list of things they can do with strings, its not about whether or not its easier to just type on its own, its about ease of use and not having to figure it out on yourself. Like i said this library is for beginners and building programs quickly by organizing things into categories.
Last edited on
Well, I could argue that <functional> kinda does these things (even if using functors).
http://www.cplusplus.com/reference/functional/
Last edited on
Sure, but this library will cover pretty much almost everything. Especially strings. Also time, math, file IO etc, shrinking huge blocks of code, making it easier to develop programs. I do think that its still prudent to learn how to do stuff like that the hard way though, so instead of just typing Concise::FileIO::SaveFile, i think people should still learn how to actually write a program that outputs to a file, but this is for beginners like i said. I want to make it easy to develop games with it as well, but that's a whole other world there, so i want to focus on console stuff for now.
There may be some merit in having functions for some more complex tasks, such as some particular usage of find+replace. A shorter way of getting random numbers using the <random> capabilities is certainly something I've found useful myself.

I'd concentrate on tasks which (a) genuinely add extra capabilities and (b) an actual need has been found (such as finding oneself needing to write similar code in multiple programs).

Personally I tend to just copy+paste functions from one program to another, and only consider making it into a part of a library when it turns up in nearly every program I write.

One of the positive things about all of this is that I find myself writing functions from the outset with the aim of making it re-usable elsewhere. Even if it is never used anywhere else, there is no extra cost.
Concise::Compare:: (I may change this to Concise::String::Compare) and intellisense will show them a list of things they can do with strings, its not about whether or not its easier to just type on its own, its about ease of use and not having to figure it out on yourself. Like i said this library is for beginners and building programs quickly by organizing things into categories.

Though I see what you're saying about intellisense , I'd suggest it is doing beginners a disservice to encourage them to do things 'the wrong way' rather than just learn the basics, which is what beginners really need to do.
Yeah, I see the point. But this should be done for more complex stuff, not comparing PODs.
Creating a library of commonly used functions/classes is a good idea, per se. Many of the facilities that the standard C++ library provides can be used as low-level building blocks for higher level abstractions. For example, the string algorithms in boost: http://www.boost.org/doc/libs/1_65_1/doc/html/string_algo/quickref.html#idp124632672
sure, I just made this library as an example to showcase what i want to do with it. I think File IO, string library stuff, maybe some template stuff, definitely make pointers easier as well, and other complexities and redundancies of C++ should be done to expedite the programming process. Suggestions are welcome. I would like to do more complex stuff but like i said, Im not great at programming unfortunately, I love doing it though but its very hard for me to grasp sometimes. I would like to get some more complex stuff done, so if anyone is interested in helping that would be great, we could just make this open source and anyone can just add to it when they please. If anyone finds that code could be made shorter, add it to the library.
Last edited on
I have updated the first post with new code. I removed comparing and added a basic find and replace. It's still early and is missing a lot of features but I think it's a good start. I also made ToString part of the String Namespace and changed it to ConvertToString
Last edited on
> I think it's a good start.

Yes.

Continue working on this. It doesn't matter if it is not perfect; you would learn a lot just by attempting to do something like this. If it is possible at all, as you add more functionality, try to get your new code peer-reviewed.
just a drive-by edit:
this
1
2
3
4
5
6
7
8
9
10
11
12
        void SaveFile(std::string fileName, std::vector<std::string>& output)
        {
            std::ofstream save;

            save.open(fileName.c_str());

            for(unsigned int i = 0; i < output.size(); i++)
            {
                save << output[i];
            }
            save.close();
        }

is missing a couple consts, makes unnecessary calls to open and close (streams support RAII), and uses the unnecessarily complex form of the for loop.

More importantly, it stores the strings back-to-back, with no separation, while your LoadFile is expecting whitespace-separated strings. It seems that you are not testing the code you're writing, you'd spot that in the first SaveFile -> LoadFile roundtrip test!

It's also missing error handling: what happens when the file can't be opened or filesystem gets full? Return something instead of that void, or throw

1
2
3
4
5
6
7
        void SaveFile(const std::string& fileName, const std::vector<std::string>& output)
        {
            std::ofstream save(fileName);
            if(!save) throw std::runtime_error("could not open file " + fileName);
            for(const auto& str: output)
                save << str << ' ';
        }


(of course using whitespace as separators also means your SaveFile -> LoadFile roundtrip will fail for a vector holding strings with spaces: {"hello world"} will return as {"hello", "world"}...
Last edited on
Avoid using so much vertical whitespace that your code becomes virtually double spaced. Here is ConciseLibrary.h with some unnecessary (IMO) whitespace removed. Compare this length (147 lines) to your original (177 lines).

I prefer more code on the screen. That way I can see more of what's going on without the need to scroll.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#ifndef TMG_H_INCLUDED
#define TMG_H_INCLUDED

#include <sstream>
#include <limits>
#include <ctime>
#include <stdlib.h>
#include <time.h>
#include <fstream>

namespace Concise {
    namespace String {
	int Find(std::string stringToSearch, const std::string wordToFind) {
	    unsigned int wordAppearance = 0;

	    for (unsigned int i = stringToSearch.find(wordToFind, 0);
		 i != std::string::npos; i = stringToSearch.find(wordToFind, i)) {
		wordAppearance++;
		i++;		//Move past the last discovered instance to avoid finding same string
	    }
	    return wordAppearance;
	}

	std::string Replace(std::string stringToSearch2, std::string & wordToFind2,
			    std::string & replaceWith)
	{
	    for (unsigned int i = stringToSearch2.find(wordToFind2, 0);
		 i != std::string::npos; i = stringToSearch2.find(wordToFind2, i)) {
		stringToSearch2.replace(i, wordToFind2.length(), replaceWith);
		i++;				 //Move past the last discovered instance to avoid finding same string
	    }
	    return stringToSearch2;
	}

	namespace ConvertToString {
	    std::string Int(int integer) {
		std::stringstream convert;
		convert << integer;

		std::string str = convert.str();
		return str;
	    }

	    std::string Float(float floatingPoint) {
		std::stringstream convert;
		convert << floatingPoint;

		std::string str = convert.str();
		return str;
	    }

	    std::string Double(double doubleNumber) {
		std::stringstream convert;
		convert << doubleNumber;

		std::string str = convert.str();
		return str;
	    }

	    std::string Char(char character) {
		std::stringstream convert;
		convert << character;

		std::string str = convert.str();

		return str;
	    }

	    void Vector(std::vector < std::string > convertVector,
			std::string & stringToUse, bool appendChar,
			std::string charToAppend)
	    {
		if (appendChar == true) {
		    for (unsigned int i = 0; i < convertVector.size(); i++) {
			stringToUse.append(convertVector[i].append(charToAppend));
		    }
		} else {
		    for (unsigned int i = 0; i < convertVector.size(); i++) {
			stringToUse.append(convertVector[i]);
		    }
		}
	    }
	}
    }

    namespace Random {
	int GetRandomNumber(int numberSize) {
	    srand(time(0));
	    int r = rand();
	    r = rand() % numberSize;
	    return r;
	}
    }

    namespace FileIO {
	void SaveFile(std::string fileName, std::vector < std::string > &output) {
	    std::ofstream save;
	    save.open(fileName.c_str());
	    for (unsigned int i = 0; i < output.size(); i++) {
		save << output[i];
	    }
	    save.close();
	}

	std::vector < std::string > LoadFile(std::string fileName,
					     std::vector < std::string > &input) {
	    std::ifstream load;
	    load.open(fileName.c_str());

	    std::string str;
	    while (load >> str) {
		input.push_back(str);
	    }
	    load.close();

	    return input;
	}
    }

    namespace Utilities {
	namespace ClearInputBuffer {
	    void ClearMaxInput(char delimiter) {
		std::cin.ignore(std::numeric_limits < std::streamsize >::max(),
				delimiter);
	    }

	    void ClearInputLimit(int ignoreLimit, char delimiter) {
		std::cin.ignore(ignoreLimit, delimiter);
	    }
	}

	namespace Time {
	    void ShowSystemTime() {
		// current date/time based on current system
		time_t now = time(0);

		// convert to string
		char *dateAndTime = ctime(&now);

		std::cout << dateAndTime;
	    }
	}
    }
}

#endif				// TMG_H_INCLUDED 

Complexity exists underneath so that clarity can exist above.

Ever look at how some of the things in the Standard Library or Boost are actually implemented? It's pretty scary, actually.

But that means that as the user of the library, life is oh so much easier.

This is exactly the point of OP's post.


The trick in C++ is that there is not a lot of overhead in startup -- only those things you ask for are available. Thus, if you wish to do something like get a random number, you must first load/initialize a library for it. Do that by using the correct constructs:

1
2
3
4
5
6
#include "my_awesome_RNG_frobber.hpp"

int main()
{
  std::cout << "Today your lucky number is " << awesome::get_random_int_in_range( 1, 100 ) << "\n";
}

All the initialization magic can be done behind the scenes, just like it is done with std::cout.

The power of C++ syntax lies in making things work simpler. For example, you can certainly provide your own methods to compare strings, say, case-insensitively:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <algorithm>
#include <cctype>
#include <ciso646>
#include <iostream>
#include <string>

struct nocase
{
  const std::string& s;
  
  nocase(): s("") { }
  nocase( const std::string& s ): s(s) { }
  
  static bool is_less_than( const std::string& a, const std::string& b )
  {
    auto ai = a.begin();
    auto bi = b.begin();
    while (ai != a.end() and bi != b.end())
      if (std::tolower( *ai++ ) != std::tolower( *bi++ ))
        return (*--ai < *--bi);
    return a.size() < b.size();
  }
  
  static bool is_equal( const std::string& a, const std::string& b )
  {
    if (a.size() != b.size()) return false;
    return mismatch( a.begin(), a.end(), b.begin(), 
      []( char a, char b ) { return std::tolower( a ) == std::tolower( b ); }
    ).first == a.end();
  }
};

bool operator < ( const nocase&      a, const std::string& b ) { return nocase::is_less_than( a.s, b   ); }
bool operator < ( const std::string& a, const nocase&      b ) { return nocase::is_less_than( a,   b.s ); }
bool operator < ( const nocase&      a, const nocase&      b ) { return nocase::is_less_than( a.s, b.s ); }

bool operator == ( const nocase&      a, const std::string& b ) { return nocase::is_equal( a.s, b   ); }
bool operator == ( const std::string& a, const nocase&      b ) { return nocase::is_equal( a,   b.s ); }
bool operator == ( const nocase&      a, const nocase&      b ) { return nocase::is_equal( a.s, b.s ); }


int main()
{
  std::string s1, s2;
  std::cout << "s1? ";  getline( std::cin, s1 );
  std::cout << "s2? ";  getline( std::cin, s2 );
  
  if (nocase(s1) == s2) 
  {
    if (s1 == s2) std::cout << "a perfect match!\n";
    else          std::cout << "match!\n";
  }
  else std::cout << "no match\n";
}

All that stuff at the top is full of boilerplate and seems like a lot of code.

But tuck that all in a library somewhere. What matters is down below in main(), line 48, where life looks pretty and easy.

* This is a very simple example, and does not exemplify a lot of things that you should do, nor does it have anywhere near the complexity necessary to properly compare two strings without case-sensitivity. JSYK.

And this gets back to one of the core principles of C++: get a library to do that. If you haven't installed Boost, do it. There are a lot of things you can do to make things prettier and easier.

The 'foreach' issue was so big that Boost had a FOREACH macro, which was often used like this:

1
2
3
4
5
6
7
8
9
10
11
12
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

int main()
{
  std::vector<int> xs ...;

  foreach (int x, xs)
  {
    do_something_with( x );
  }
}

So very useful, that now it is programmed into the language:
1
2
3
4
5
6
7
8
9
int main()
{
  std::vector<int> xs ...;

  for (int x : xs)
  {
    do_something_with( x );
  }
}

Backporting? Easy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifdef BOOST_FOREACH
  #define for_ BOOST_FOREACH
#else
  #define for_( x, xs ) for ( x : xs )
#endif

int main()
{
  std::vector<int> xs ...

  for_( int x, xs )
  {
    do_something_with( x );
  }
}

All the complexity belongs in libraries. Your code should be pretty!


This has been a rambling post, full of randomness. Your random number for today is 92.
Topic archived. No new replies allowed.