Re: Reverse comma operator?
Kaz Kylheku wrote:
On 2009-08-10, Paul N <gw7rib@aol.com> wrote:
I had an idea the other day for a new operator for C and C++, which
acts like the comma operator but which returns the first value instead
of the second.
You mean like PROG1 in Common Lisp? Quite useful indeed.
In C we have kind of a special case of this, namely post-increment. I.e.
y = x++;
is similar to a use of your $ operator:
y = x $ x++;
Implicit to a saved copy of some prior value of a computation is sometimes a
handy way to express yourself.
For example, and using $ for my operator as it doesn't
seem to be already used,
return setupstuff() , calculatevalue() $ resetstuff();
Lisp:
(progn (set-up-stuff)
(prog1 (calculate-value)
(reset-stuff)))
There is prog2 also, (but no prog3, just 1, 2 and n).
I'm pretty sure you can't emulate this operator in any way in portable C.
In the GNU C dialect, we can use the ``typeof'' operator to figure out the
return type of the expression, so that we can define a temporary variable
of a compatible type. And GNU C has block statements which return a value
(the value of the last statement in the block), similar to Lisp's PROG.
(GNU C was originally written by Lisp hackers). So in GNU C, we can easily make:
#define PROG1(A, B) ...
which evaluates A, then B, with a sequence point, and yields the value of A.
I can't think of a way to do this in ISO C. Even if we accept this ugly
interface:
#define PROG1(TYPEOF_A, A, B)
You inspired me to have a go (and I've not really succeeded 100%) at
doing this using variadic templates (a learning exercise for me if
nothing else!)
#include <iostream>
// Based on simple_tuple from http://www.devx.com/cplus/Article/41533/1954
template <typename ... Types>
class ParamSet;
template <>
class ParamSet<> {};
template <typename First, typename ... Rest>
class ParamSet<First,Rest...> : private ParamSet<Rest...>
{
First member;
public:
ParamSet(First const& f, Rest const& ... rest):
ParamSet<Rest...>(rest...), member(f) { }
operator First() const { return member; }
};
template <typename Ret, typename... Args>
Ret dispatch(Ret (*f)(Args...), const ParamSet<Args...>& args) {
return f(args);
}
template <typename Ret>
Ret dispatch(Ret (*f)(), const ParamSet<>&) {
return f();
}
template <typename Ret, typename... Args1, typename... Args2>
Ret first(Ret (*f1)(Args1...), Ret (*f2)(Args2...), const
ParamSet<Args1...>& args1, const ParamSet<Args2...>& args2) {
const Ret& val = dispatch(f1,args1);
dispatch(f2,args2);
return val;
}
template <typename ... Types>
class ParamSet<Types...> make_param(const Types&... types) { return
ParamSet<Types...>(types...); }
// no parenthesis on a1, a2 is important to avoid operator comma with
// multiple parameters here.
#define prog1(f1,f2,a1,a2) first(f1,f2,make_param a1,make_param a2)
bool test1(void*) {
std::cout << "in test1()" << std::endl;
return true;
}
bool test2() {
std::cout << "in test2()" << std::endl;
return false;
}
int main() {
std::cout << prog1(test1, test2, ((void*)NULL),()) << std::endl;
return 0;
}
Can anyone improve it? The problem I have is dispatching with more than
1 argument. I also can't quite think of a tidy way to 'steal' the
arguments in a macro and make the macro just take two parameters instead
of 4.
Alan