Is it possible??

Hello

I was wondering if it possible to create custom functions like for, for_each, while etc.

For example if one wants to create another version of the for function that would take only parameter.

How would I do something like that?
Think of a hypothetical use-case for your custom version of for. Then show us an example usage.

We cannot change the meaning of the language keyword for in a C++ program. Language keywords are intrinsic to C++ itself; they can't be modified.

See this introduction to functions:
http://www.cplusplus.com/doc/tutorial/functions/
Last edited on
a macro or other expressions can sort of be made to look like a key-word, but its fairly nasty code.
@mbozzi
I don't want o modify a c++ keyword as I know this is not possible, what I want to do is create a new one that satisfies my needs

@jonnin
can you give some example?
You certainly could, I mean I dont see why not. I created my own getline function because I needed a getline that took a string instead of a char as a delimiter, you just cant name it the same as a keyword, unless you use uppercase, so for example my getline was Getline, you cant do getline. it all depends on what functionality you want it to serve.
Avoid macros as far as possible; in the vast majority of cases, there are better alternatives.

"Scream when you see a macro that isn’t just used for source control (e.g., #ifdef)"
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-macros2

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
#include <iostream>
#include <string>

template < typename T, typename CONDITION, typename INCREMENT, typename BODY, typename... EXTRA_ARGS >
void for_( T init, CONDITION condition, INCREMENT increment, BODY body, EXTRA_ARGS&&... extra_args )
{
    for( ; condition(init) ; increment(init) )
        body( init, std::forward<EXTRA_ARGS>(extra_args)... ) ;
}

int main()
{
    // for( int i = 10 ; i < 80 ; i += i/4 ) std::cout << i << ' ' ;
    for_( 10, []( int i ) { return i < 80 ; }, []( int& i ) { i += i/4 ; }, []( int i ) { std::cout << i << ' ' ; } ) ;

    std::cout << "\n\n" ;

    for_( std::string( "hello world" ), // init

          []( const std::string& str ) { return str.size() < 20 ; }, // condition

          []( std::string& str ) { str += '!' ; }, // increment

          []( const std::string& str, const auto& prefix, const auto& suffix )
              { std::cout << prefix << str << suffix << '\n' ; }, // body

          "*** - '", "' - ###" ) ; // extra args (prefix and suffix)
}

http://coliru.stacked-crooked.com/a/23cef1b9146aeebe
Another example of a function that is a wrapper for for:
http://www.cplusplus.com/reference/algorithm/for_each/
I would rather not do it with a macro. They don't debug properly (because the code is swapped in it can't 'go into' the macro in the debugger, so its hard to unravel) and they lose types (macros ignore type safety) and other issues. I was just saying you 'can'.

What, exactly, can you not do with the many, many different loops available already?
Last edited on
@jonnin
there's nothing that I want to do that the existing loops won't do it. I am just curious to learn how they work and if I ever need to create my own.
In this example, I want to to create a for that only takes one parameter, an integer.
Instead of writing
 
for (int i = 0; i < 50; ++i)

I would create a for version like this
 
for_(50)

and they would act the same

@JLBorges
The example you gave is great, I have tested and it works fine ( I am still trying to understand how it exactly works though).
But is there a cleaner and more intuitive way of doing that, that the syntax is like the original for??
Last edited on
the above is where a macro is very easy and fairly clean (the MACRO risks are removed, its just syntax sugar, but its still not good see comments).

#define for_(c) for(int i = 0; i < c; i++)
which I didn't test, but its close to what you ask. you should make 'i' something unique / unlikely to collide with other variable names, like i_for_variable, and its now very weird because your use looks like this:

for_(100)
{
cout << somevector[i]; //what? where did i come from?!
}

with iterator or pointer its a little less odd...
for_(100)
{
cout << *pointer;
pointer++;
}

this is generally still a bad idea: it makes c++ coders unable to read your code, they have to look up the macro. That isnt too bad for ONE, but if you had 10 different ones... it would quickly become gibberish. But you 'could' do this. And again, the i from nowhere is strange.

its not quite a range-based loop, but almost. are you aware of those?
Last edited on
But is there a cleaner and more intuitive way of doing that, that the syntax is like the original for??
Use a range-based for-loop in combination with a library:
1
2
3
4
for (auto i : std::ranges::iota_view({}, 50))
{
  std::cout << i << '\n';
}

https://godbolt.org/z/_RVuGM

Shorten that as you want, e.g.
1
2
constexpr auto below = std::bind_front(std::ranges::views::iota, 0); 
for (auto i : below(50)) { /* ... */ }

https://godbolt.org/z/CQTjdJ
Last edited on
@jonnin
The example with macro does exactly what I want, but it is really a shame the problems that come up with it, specially because of the variable i.
If I am to use it some day I will be sure to be aware of those problems, this is kind of code I will use when I know I am the only one who will use that code, even I will have it well commented just in case.
A good thing about it though is that I will save some code whenever I use for, beacuse I use for a lot.

I think that answers my question.

I also posted this question on stackoverflow and it had some nice answers https://stackoverflow.com/questions/59252643/how-to-create-my-own-loop-version-in-c

Thank you guys, I really appreciate it

Last edited on
C++ and the inherited bits of C that come with it give you a ton of power, but they also give you a TON of things that you CAN do but probably should not. Sure, its your code for you, but habits like this become trouble if you need to do something for a job. If you just code as a hobby, anything goes, I guess.

we all use for loops a lot. And we all just type out what the language requires. I highly recommend you focus your laziness on something else... its good to be lazy (write less code, work smarter not harder) but there are some shortcuts that are just more trouble than they are worth. Use the ranged based loop if you want something a little shorter for 0-n type loops.
The problem with coding your own loops is that nobody else will know what they without digging into the implementation. This sort of thing has a habit of growing, becoming malignant and then metastasizing. Consider the following, which I encountered at work a few years back. it took me hours to figure out what it did. Scroll to the bottom [edit - next post because it's too long] for the answer.
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#ifndef _RpcRetry_h
#define _RpcRetry_h

#include <vector>
#include <bstring.h>

/**
 * A simple helper class to control how many times an rpc related service
 * is attempted and how long to sleep between failed attempts.
 */
class RpcRetry
{

  public:

    /**
     * The base class for rpc retry functors.
     */
    class Functor
    {

      public:

	virtual bool operator()() = 0;

    };

    /**
     * A class to encapsulate errors.
     */
    class Error
    {

      public:

	/**
         * Constructor.
         */
	Error():m_errno(0), m_reason(0)
	{
	}

	/**
         * Set the errno (this will be specific to the underlying rpc call).
         *
         * @param theErrno the errno.
         */
	virtual void setErrno(int theErrno)
	{
	    m_errno = theErrno;
	}

	/**
         * Get the errno.
         *
         * @return the errno.
         */
	virtual int getErrno() const
	{
	    return (m_errno);
	}

	/**
         * Set the reason (this will be specific to the underlying rpc call).
         *
         * @param theReason the reason.
         */
	virtual void setReason(int theReason)
	{
	    m_reason = theReason;
	}

	/**
         * Get the reason.
         *
         * @return the reason.
         */
	virtual int getReason() const
	{
	    return (m_reason);
	}

	/**
         * Set the error detail string.
         *
         * @param theDetail the error detail string.
         */
	virtual void setDetail(char const *theMessage, ...);

	/**
         * Get the error detail string.
         *
         * @return the error detail string.
         */
	char const *getDetail() const
	{
	    return (m_detail.c_str());
	}

      private:

	int m_errno;

	int m_reason;

	string m_detail;

    };

    /**
     * Default constructor.
     */
    RpcRetry() {
	m_numAttempts = getDefaultNumAttempts();
	m_sleep = getDefaultSleep();
    }

    /**
     * Sets the number of tries.
     *
     * @param numAttempts the number of attempts.
     */
    void setNumAttempts(int numAttempts)
    {
	m_numAttempts = numAttempts;
    }

    /**
     * Gets the number of attempts.
     *
     * @return the default number of attempts.
     */
    int getNumAttempts() const
    {
	return (m_numAttempts);
    }

    /**
     * Set the between attempt sleep value.
     *
     * @param numSeconds the number of seconds to sleep (0 for no sleep).
     */
    void setSleep(unsigned int numSeconds)
    {
	m_sleep = numSeconds;
    }

    /**
     * Get the between attempt sleep value.
     *
     * @return the number of seconds to sleep between attempts.
     */
    unsigned int getSleep() const
    {
	return (m_sleep);
    }

    /**
     * Access the rpc error.
     *
     * @return the rpc error.
     */
    Error & getRpcError()
    {
	return (m_error);
    }

    /**
     * This will call the functor provided until either the operation
     * completes okay (return from functor is true) or the number of
     * attempts is exhausted.
     *
     * @param theFunctor the functor to invoke.
     * @return true if functor succeeded, false otherwise.
     */
    bool doRpc(Functor & theFunctor);

    /**
     * Sets the default number of tries.
     *
     * @param numAttempts the number of attempts for default.
     */
    static void setDefaultNumAttempts(int numAttempts)
    {
	m_defaultNumAttempts = numAttempts;
    }

    /**
     * Gets the default number of attempts.
     *
     * @return the default number of attempts.
     */
    static int getDefaultNumAttempts()
    {
	return (m_defaultNumAttempts);
    }

    /**
     * Set the default between attempt sleep value.
     *
     * @param numSeconds the number of seconds to sleep (0 for no sleep).
     */
    static void setDefaultSleep(unsigned int numSeconds)
    {
	m_defaultSleep = numSeconds;
    }

    /**
     * Get the default between attempt sleep value.
     *
     * @return the number of seconds to sleep between attempts.
     */
    static unsigned int getDefaultSleep()
    {
	return (m_defaultSleep);
    }

  private:

    static int m_defaultNumAttempts;	// default number of attempts

    static unsigned int m_defaultSleep;	// default between attempt sleep

    int m_numAttempts;		// instance number of attempts

    unsigned int m_sleep;	// instance between attempt sleep

    Error m_error;		// a placeholder for reporting errors.

};

/**
 * A no argument rpc retry functor.
 */
template < class O > class TRpcRetryFunctor0:public RpcRetry::Functor {

  public:

  TRpcRetryFunctor0(O & theO, bool(O::*theOMethod) ()):
    m_o(&theO), m_f(theOMethod) {
    }

    virtual bool
    operator() ()
    {
	return ((*m_o.*m_f) ());
    }

  private:

    O * m_o;					 // the object

    bool(O::*m_f) ();				 // the method

};

/**
 * A single argument rpc retry functor.
 */
template < class O, class T1 > class TRpcRetryFunctor1:public RpcRetry::Functor {

  public:

  TRpcRetryFunctor1(O & theO, bool(O::*theOMethod) (T1), T1 theT1):
    m_o(&theO), m_f(theOMethod), m_t1(theT1) {
    }

    virtual bool
    operator() ()
    {
	return ((*m_o.*m_f) (m_t1));
    }

  private:

    O * m_o;					 // the object

    bool(O::*m_f) (T1);				 // the method

    T1	m_t1;			// the only argument

};

/**
 * A two argument rpc retry functor.
 */
template < class O, class T1, class T2 > class TRpcRetryFunctor2:public RpcRetry::Functor {

  public:

  TRpcRetryFunctor2(O & theO, bool(O::*theOMethod) (T1, T2), T1 theT1, T2 theT2):
    m_o(&theO), m_f(theOMethod), m_t1(theT1), m_t2(theT2) {
    }

    virtual bool
    operator() ()
    {
	return ((*m_o.*m_f) (m_t1, m_t2));
    }

  private:

    O * m_o;					 // the object

    bool(O::*m_f) (T1, T2);			 // the method

    T1	m_t1;			// the first argument

    T2	m_t2;			// the second argument

};

/**
 * A three argument rpc retry functor.
 */
template < class O, class T1, class T2, class T3 > class TRpcRetryFunctor3:public RpcRetry::
    Functor {

  public:

  TRpcRetryFunctor3(O & theO, bool(O::*theOMethod) (T1, T2, T3), T1 theT1, T2 theT2, T3 theT3):
    m_o(&theO), m_f(theOMethod), m_t1(theT1), m_t2(theT2), m_t3(theT3) {
    }

    virtual bool
    operator() ()
    {
	return ((*m_o.*m_f) (m_t1, m_t2, m_t3));
    }

  private:

    O * m_o;					 // the object

    bool(O::*m_f) (T1, T2, T3);			 // the method

    T1	m_t1;			// the first argument

    T2	m_t2;			// the second argument

    T3	m_t3;			// the third argument

};

/**
 * A four argument rpc retry functor.
 */
template < class O, class T1, class T2, class T3, class T4 > class TRpcRetryFunctor4:public RpcRetry::
    Functor
{

  public:

  TRpcRetryFunctor4(O & theO, bool(O::*theOMethod) (T1, T2, T3, T4), T1 theT1, T2 theT2, T3 theT3, T4 theT4):
    m_o(&theO), m_f(theOMethod), m_t1(theT1), m_t2(theT2), m_t3(theT3), m_t4(theT4) {
    }

    virtual bool
    operator() ()
    {
	return ((*m_o.*m_f) (m_t1, m_t2, m_t3, m_t4));
    }

  private:

    O * m_o;					 // the object

    bool(O::*m_f) (T1, T2, T3, T4);		 // the method

    T1	m_t1;			// the first argument

    T2	m_t2;			// the second argument

    T3	m_t3;			// the third argument

    T4	m_t4;			// the fourth argument

};

#endif 

[ continuation of previous post ]

RpcRetry.cpp:
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 "RpcRetry.h"
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>


int RpcRetry::m_defaultNumAttempts = 6;
unsigned int RpcRetry::m_defaultSleep = 2;


void RpcRetry::Error::setDetail(char const *theMessage,
                                ...) {
    va_list ap;
    va_start(ap, theMessage);
    char theBuffer[4096];
    // where the f*** is vsnprintf?
    ::vsprintf(theBuffer, theMessage, ap);
    m_detail = theBuffer;
    va_end(ap);
}


bool RpcRetry::doRpc(Functor &theFunctor) {
    bool theResult = false;
    for (int i = 0; i < getNumAttempts() && !theResult; i++) {
        theResult = theFunctor();
        if (!theResult && (i + 1) < getNumAttempts()) {
            ::sleep(getSleep());
        }
    }
    return (theResult);
}

Great code huh? It's templatized! It's accessorized! It's functorized! What is it?

IT'S A !@*#$&@@@(!!+!$ FOR LOOP!!!

This code has a special place in my archive of bad code. Don't let your custom loop end up there too. :)
Your posts make me realize how happy I am that I only program in C++ for fun, and use other languages at work. That being said, I've seen similar things at work (as in, it's not like other languages are uniquely immune to this), but they were at least more complicated than... a for loop.

1
2
3
4
5
6
7
    T1	m_t1;			// the first argument

    T2	m_t2;			// the second argument

    T3	m_t3;			// the third argument

    T4	m_t4;			// the fourth argument 
Those variables names... and those comments... lol.
Last edited on
I have mixed feelings on variables. Coming in from math, 1 letter variables made sense for a lot of what we did. Try writing something as simple as the quadratic equation with good variable names... you can't visualize the equation for the bloat. Everything has a time and place.
Last edited on
@dhayden
Great example, just looking at that code I can feel the pain you must've been through ahahahah but as I said my custom for will be for my own use, me alone. But it is good to know what people can do to make things hard hahah

@jonnin
I want to become a professional C++ programmer, I will be sure to take these into account. C++ is just my favorite language even though tons of people run away from it. I will be learning other languages in the near future but c++ will always my favorite.
Thanks again for the tips, they're really helpful!
Topic archived. No new replies allowed.