Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 11ca9ffc authored by Lajos Molnar's avatar Lajos Molnar
Browse files

stagefright: add param utils for Codec2 VNDK

Utility macros to implement enumeration reflection

Bug: 30262321
Change-Id: I31b413d9de3823b16a0e5a30e0d335762e234364
parent 29a6ba99
Loading
Loading
Loading
Loading
+302 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef C2UTILS_PARAM_UTILS_H_
#define C2UTILS_PARAM_UTILS_H_

#include <C2Param.h>
#include <util/_C2MacroUtils.h>

#include <iostream>

/** \file
 * Utilities for parameter handling to be used by Codec2 implementations.
 */

namespace android {

/// \cond INTERNAL

/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */

/**
 * Utility class that allows ignoring enum value assignment (e.g. both '(_C2EnumConst)kValue = x'
 * and '(_C2EnumConst)kValue' will eval to kValue.
 */
template<typename T>
class _C2EnumConst {
public:
    // implicit conversion from T
    inline _C2EnumConst(T value) : _mValue(value) {}
    // implicit conversion to T
    inline operator T() { return _mValue; }
    // implicit conversion to C2Value::Primitive
    inline operator C2Value::Primitive() { return (T)_mValue; }
    // ignore assignment and return T here to avoid implicit conversion to T later
    inline T &operator =(T value __unused) { return _mValue; }
private:
    T _mValue;
};

/// mapper to get name of enum
/// \note this will contain any initialization, which we will remove when converting to lower-case
#define _C2_GET_ENUM_NAME(x, y) #x
/// mapper to get value of enum
#define _C2_GET_ENUM_VALUE(x, type) (_C2EnumConst<type>)x

/// \endcond

#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
    return C2ParamUtils::sanitizeEnumValues( \
            std::vector<C2Value::Primitive> { _C2_MAP(_C2_GET_ENUM_VALUE, type, __VA_ARGS__) }, \
            { _C2_MAP(_C2_GET_ENUM_NAME, type, __VA_ARGS__) }, \
            prefix); \
}

#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...) \
template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
    return C2ParamUtils::customEnumValues( \
            std::vector<std::pair<C2StringLiteral, name>> names); \
}


class C2ParamUtils {
private:
    static size_t countLeadingUnderscores(C2StringLiteral a) {
        size_t i = 0;
        while (a[i] == '_') {
            ++i;
        }
        return i;
    }

    static size_t countMatching(C2StringLiteral a, const C2String &b) {
        for (size_t i = 0; i < b.size(); ++i) {
            if (!a[i] || a[i] != b[i]) {
                return i;
            }
        }
        return b.size();
    }

    // ABCDef => abc-def
    // ABCD2ef => abcd2-ef // 0
    // ABCD2Ef => ancd2-ef // -1
    // AbcDef => abc-def // -1
    // Abc2Def => abc-2def
    // Abc2def => abc-2-def
    // _Yo => _yo
    // _yo => _yo
    // C2_yo => c2-yo
    // C2__yo => c2-yo

    static C2String camelCaseToDashed(C2String name) {
        enum {
            kNone = '.',
            kLower = 'a',
            kUpper = 'A',
            kDigit = '1',
            kDash = '-',
            kUnderscore = '_',
        } type = kNone;
        size_t word_start = 0;
        for (size_t ix = 0; ix < name.size(); ++ix) {
            /* std::cout << name.substr(0, word_start) << "|"
                    << name.substr(word_start, ix - word_start) << "["
                    << name.substr(ix, 1) << "]" << name.substr(ix + 1)
                    << ": " << (char)type << std::endl; */
            if (isupper(name[ix])) {
                if (type == kLower) {
                    name.insert(ix++, 1, '-');
                    word_start = ix;
                }
                name[ix] = tolower(name[ix]);
                type = kUpper;
            } else if (islower(name[ix])) {
                if (type == kDigit && ix > 0) {
                    name.insert(ix++, 1, '-');
                    word_start = ix;
                } else if (type == kUpper && ix > word_start + 1) {
                    name.insert(ix++ - 1, 1, '-');
                    word_start = ix - 1;
                }
                type = kLower;
            } else if (isdigit(name[ix])) {
                if (type == kLower) {
                    name.insert(ix++, 1, '-');
                    word_start = ix;
                }
                type = kDigit;
            } else if (name[ix] == '_') {
                if (type == kDash) {
                    name.erase(ix--, 1);
                } else if (type != kNone && type != kUnderscore) {
                    name[ix] = '-';
                    type = kDash;
                    word_start = ix + 1;
                } else {
                    type = kUnderscore;
                    word_start = ix + 1;
                }
            } else {
                name.resize(ix);
            }
        }
        // std::cout << "=> " << name << std::endl;
        return name;
    }

    static std::vector<C2String> sanitizeEnumValueNames(
            const std::vector<C2StringLiteral> names,
            C2StringLiteral _prefix = NULL) {
        std::vector<C2String> sanitizedNames;
        C2String prefix;
        size_t extraUnderscores = 0;
        bool first = true;
        if (_prefix) {
            extraUnderscores = countLeadingUnderscores(_prefix);
            prefix = _prefix + extraUnderscores;
            first = false;
            // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
        }

        // calculate prefix and minimum leading underscores
        for (C2StringLiteral s : names) {
            // std::cout << s << std::endl;
            size_t underscores = countLeadingUnderscores(s);
            if (first) {
                extraUnderscores = underscores;
                prefix = s + underscores;
                first = false;
            } else {
                size_t matching = countMatching(
                    s + underscores,
                    prefix);
                prefix.resize(matching);
                extraUnderscores = std::min(underscores, extraUnderscores);
            }
            // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
            if (prefix.size() == 0 && extraUnderscores == 0) {
                break;
            }
        }

        // we swallow the first underscore after upper case prefixes
        bool upperCasePrefix = true;
        for (size_t i = 0; i < prefix.size(); ++i) {
            if (islower(prefix[i])) {
                upperCasePrefix = false;
                break;
            }
        }

        for (C2StringLiteral s : names) {
            size_t underscores = countLeadingUnderscores(s);
            C2String sanitized = C2String(s, underscores - extraUnderscores);
            sanitized.append(s + prefix.size() + underscores +
                        (upperCasePrefix && s[prefix.size() + underscores] == '_'));
            sanitizedNames.push_back(camelCaseToDashed(sanitized));
        }

        for (C2String s : sanitizedNames) {
            std::cout << s << std::endl;
        }

        return sanitizedNames;
    }

    friend class C2ParamTest_ParamUtilsTest_Test;

public:
    static std::vector<C2String> getEnumValuesFromString(C2StringLiteral value) {
        std::vector<C2String> foundNames;
        size_t pos = 0, len = strlen(value);
        do {
            size_t endPos = strcspn(value + pos, " ,=") + pos;
            if (endPos > pos) {
                foundNames.emplace_back(value + pos, endPos - pos);
            }
            if (value[endPos] && value[endPos] != ',') {
                endPos += strcspn(value + endPos, ",");
            }
            pos = strspn(value + endPos, " ,") + endPos;
        } while (pos < len);
        return foundNames;
    }

    template<typename T>
    static C2FieldDescriptor::named_values_type sanitizeEnumValues(
            std::vector<T> values,
            std::vector<C2StringLiteral> names,
            C2StringLiteral prefix = NULL) {
        C2FieldDescriptor::named_values_type namedValues;
        std::vector<C2String> sanitizedNames = sanitizeEnumValueNames(names, prefix);
        for (size_t i = 0; i < values.size() && i < sanitizedNames.size(); ++i) {
            namedValues.emplace_back(sanitizedNames[i], values[i]);
        }
        return namedValues;
    }

    template<typename E>
    static C2FieldDescriptor::named_values_type customEnumValues(
            std::vector<std::pair<C2StringLiteral, E>> items) {
        C2FieldDescriptor::named_values_type namedValues;
        for (auto &item : items) {
            namedValues.emplace_back(item.first, item.second);
        }
        return namedValues;
    }
};

/* ---------------------------- UTILITIES FOR PARAMETER REFLECTION ---------------------------- */

/* ======================== UTILITY TEMPLATES FOR PARAMETER REFLECTION ======================== */

#if 1
template<typename... Params>
class C2_HIDE _C2Tuple { };

C2_HIDE
void addC2Params(std::list<const C2FieldDescriptor> &, _C2Tuple<> *) {
}

template<typename T, typename... Params>
C2_HIDE
void addC2Params(std::list<const C2FieldDescriptor> &fields, _C2Tuple<T, Params...> *)
{
    //C2Param::index_t index = T::baseIndex;
    //(void)index;
    fields.insert(fields.end(), T::fieldList);
    addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
}

template<typename... Params>
C2_HIDE
std::list<const C2FieldDescriptor> describeC2Params() {
    std::list<const C2FieldDescriptor> fields;
    addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
    return fields;
}

#endif

/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */

}  // namespace android

#endif  // C2UTILS_PARAM_UTILS_H_
+162 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef C2UTILS_MACRO_UTILS_H_
#define C2UTILS_MACRO_UTILS_H_

/** \file
 * Macro utilities for the utils library used by Codec2 implementations.
 */

/// \if 0

/* --------------------------------- VARIABLE ARGUMENT COUNTING --------------------------------- */

// remove empty arguments - _C2_ARG() expands to '', while _C2_ARG(x) expands to ', x'
// _C2_ARGn(...) does the same for n arguments
#define _C2_ARG(...) , ##__VA_ARGS__
#define _C2_ARG2(_1, _2) _C2_ARG(_1) _C2_ARG(_2)
#define _C2_ARG4(_1, _2, _3, _4) _C2_ARG2(_1, _2) _C2_ARG2(_3, _4)
#define _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG4(_1, _2, _3, _4) _C2_ARG4(_5, _6, _7, _8)
#define _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
        _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG8(_9, _10, _11, _12, _13, _14, _15, _16)

// return the 65th argument
#define _C2_ARGC_3(_, _0, _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, ...) _64

/// \endif

/**
 * Returns the number of arguments.
 */
// We do this by prepending 1 and appending 65 designed values such that the 65th element
// will be the number of arguments.
#define _C2_ARGC(...) _C2_ARGC_1(0, ##__VA_ARGS__, \
        64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, \
        42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
        20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

/// \if 0

// step 1. remove empty arguments - this is needed to allow trailing comma in enum definitions
// (NOTE: we don't know which argument will have this trailing comma so we have to try all)
#define _C2_ARGC_1(_, _0, _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, ...) \
    _C2_ARGC_2(_ _C2_ARG(_0) \
    _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
    _C2_ARG16(_17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) \
    _C2_ARG16(_33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) \
    _C2_ARG16(_49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64), \
    ##__VA_ARGS__)

// step 2. this is needed as removed arguments cannot be passed directly as empty into a macro
#define _C2_ARGC_2(...) _C2_ARGC_3(__VA_ARGS__)

/// \endif

/* -------------------------------- VARIABLE ARGUMENT CONVERSION -------------------------------- */

/// \if 0

// macros that convert _1, _2, _3, ... to fn(_1, arg), fn(_2, arg), fn(_3, arg), ...
#define _C2_MAP_64(fn, arg, head, ...) fn(head, arg), _C2_MAP_63(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_63(fn, arg, head, ...) fn(head, arg), _C2_MAP_62(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_62(fn, arg, head, ...) fn(head, arg), _C2_MAP_61(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_61(fn, arg, head, ...) fn(head, arg), _C2_MAP_60(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_60(fn, arg, head, ...) fn(head, arg), _C2_MAP_59(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_59(fn, arg, head, ...) fn(head, arg), _C2_MAP_58(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_58(fn, arg, head, ...) fn(head, arg), _C2_MAP_57(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_57(fn, arg, head, ...) fn(head, arg), _C2_MAP_56(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_56(fn, arg, head, ...) fn(head, arg), _C2_MAP_55(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_55(fn, arg, head, ...) fn(head, arg), _C2_MAP_54(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_54(fn, arg, head, ...) fn(head, arg), _C2_MAP_53(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_53(fn, arg, head, ...) fn(head, arg), _C2_MAP_52(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_52(fn, arg, head, ...) fn(head, arg), _C2_MAP_51(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_51(fn, arg, head, ...) fn(head, arg), _C2_MAP_50(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_50(fn, arg, head, ...) fn(head, arg), _C2_MAP_49(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_49(fn, arg, head, ...) fn(head, arg), _C2_MAP_48(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_48(fn, arg, head, ...) fn(head, arg), _C2_MAP_47(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_47(fn, arg, head, ...) fn(head, arg), _C2_MAP_46(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_46(fn, arg, head, ...) fn(head, arg), _C2_MAP_45(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_45(fn, arg, head, ...) fn(head, arg), _C2_MAP_44(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_44(fn, arg, head, ...) fn(head, arg), _C2_MAP_43(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_43(fn, arg, head, ...) fn(head, arg), _C2_MAP_42(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_42(fn, arg, head, ...) fn(head, arg), _C2_MAP_41(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_41(fn, arg, head, ...) fn(head, arg), _C2_MAP_40(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_40(fn, arg, head, ...) fn(head, arg), _C2_MAP_39(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_39(fn, arg, head, ...) fn(head, arg), _C2_MAP_38(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_38(fn, arg, head, ...) fn(head, arg), _C2_MAP_37(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_37(fn, arg, head, ...) fn(head, arg), _C2_MAP_36(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_36(fn, arg, head, ...) fn(head, arg), _C2_MAP_35(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_35(fn, arg, head, ...) fn(head, arg), _C2_MAP_34(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_34(fn, arg, head, ...) fn(head, arg), _C2_MAP_33(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_33(fn, arg, head, ...) fn(head, arg), _C2_MAP_32(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_32(fn, arg, head, ...) fn(head, arg), _C2_MAP_31(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_31(fn, arg, head, ...) fn(head, arg), _C2_MAP_30(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_30(fn, arg, head, ...) fn(head, arg), _C2_MAP_29(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_29(fn, arg, head, ...) fn(head, arg), _C2_MAP_28(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_28(fn, arg, head, ...) fn(head, arg), _C2_MAP_27(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_27(fn, arg, head, ...) fn(head, arg), _C2_MAP_26(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_26(fn, arg, head, ...) fn(head, arg), _C2_MAP_25(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_25(fn, arg, head, ...) fn(head, arg), _C2_MAP_24(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_24(fn, arg, head, ...) fn(head, arg), _C2_MAP_23(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_23(fn, arg, head, ...) fn(head, arg), _C2_MAP_22(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_22(fn, arg, head, ...) fn(head, arg), _C2_MAP_21(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_21(fn, arg, head, ...) fn(head, arg), _C2_MAP_20(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_20(fn, arg, head, ...) fn(head, arg), _C2_MAP_19(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_19(fn, arg, head, ...) fn(head, arg), _C2_MAP_18(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_18(fn, arg, head, ...) fn(head, arg), _C2_MAP_17(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_17(fn, arg, head, ...) fn(head, arg), _C2_MAP_16(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_16(fn, arg, head, ...) fn(head, arg), _C2_MAP_15(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_15(fn, arg, head, ...) fn(head, arg), _C2_MAP_14(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_14(fn, arg, head, ...) fn(head, arg), _C2_MAP_13(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_13(fn, arg, head, ...) fn(head, arg), _C2_MAP_12(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_12(fn, arg, head, ...) fn(head, arg), _C2_MAP_11(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_11(fn, arg, head, ...) fn(head, arg), _C2_MAP_10(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_10(fn, arg, head, ...) fn(head, arg), _C2_MAP_9(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_9(fn, arg, head, ...) fn(head, arg), _C2_MAP_8(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_8(fn, arg, head, ...) fn(head, arg), _C2_MAP_7(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_7(fn, arg, head, ...) fn(head, arg), _C2_MAP_6(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_6(fn, arg, head, ...) fn(head, arg), _C2_MAP_5(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_5(fn, arg, head, ...) fn(head, arg), _C2_MAP_4(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_4(fn, arg, head, ...) fn(head, arg), _C2_MAP_3(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_3(fn, arg, head, ...) fn(head, arg), _C2_MAP_2(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_2(fn, arg, head, ...) fn(head, arg), _C2_MAP_1(fn, arg, ##__VA_ARGS__)
#define _C2_MAP_1(fn, arg, head, ...) fn(head, arg)

/// \endif

/**
 * Maps each argument using another macro x -> fn(x, arg)
 */
// use wrapper to call the proper mapper based on the number of arguments
#define _C2_MAP(fn, arg, ...) _C2_MAP__(_C2_ARGC(__VA_ARGS__), fn, arg, ##__VA_ARGS__)

/// \if 0

// evaluate _n so it becomes a number
#define _C2_MAP__(_n, fn, arg, ...) _C2_MAP_(_n, fn, arg, __VA_ARGS__)
// call the proper mapper
#define _C2_MAP_(_n, fn, arg, ...) _C2_MAP_##_n (fn, arg, __VA_ARGS__)

/// \endif

#endif  // C2UTILS_MACRO_UTILS_H_