2

I wrote my "Result" macro header, using C23 and some of the newest features:

#pragma once

#include <stdio.h>
#include <stdlib.h>

#define __RESULT_EAT_PARENS(...) __VA_ARGS__

typedef enum ResultKind
{
    OK,
    ERR
} ResultKind;

#define DEFINE_RESULT(ok_type, err_type)         \
    typedef struct Result_##ok_type##_##err_type \
    {                                            \
        ResultKind kind;                         \
        union                                    \
        {                                        \
            ok_type ok;                          \
            err_type err;                        \
        } value;                                 \
    } Result_##ok_type##_##err_type;

#define __RESULT_OK_PASTE(ok_type, err_type, ...) \
    (Result_##ok_type##_##err_type)               \
    {                                             \
        .kind = OK, .value = {.ok = __VA_ARGS__ } \
    }

#define __RESULT_OK_IMPL(ok_type, err_type, ...) \
    __RESULT_OK_PASTE(ok_type, err_type, __VA_ARGS__)

#define __RESULT_ERR_PASTE(ok_type, err_type, ...)  \
    (Result_##ok_type##_##err_type)                 \
    {                                               \
        .kind = ERR, .value = {.err = __VA_ARGS__ } \
    }

#define __RESULT_ERR_IMPL(ok_type, err_type, ...) \
    __RESULT_ERR_PASTE(ok_type, err_type, __VA_ARGS__)

#define Ok(types, ...) \
    __RESULT_OK_IMPL(__RESULT_EAT_PARENS types, (__VA_ARGS__))

#define Err(types, ...) \
    __RESULT_ERR_IMPL(__RESULT_EAT_PARENS types, (__VA_ARGS__))

#define is_ok(res) \
    ((res).kind == OK)

#define is_err(res) \
    ((res).kind == ERR)

#define expect(res, msg)                                                                                        \
    ({                                                                                                          \
        __auto_type __res_tmp = (res);                                                                          \
        (is_err(__res_tmp))                                                                                     \
            ? (fprintf(stderr, "Panic at %s:%d: %s\n", __FILE__, __LINE__, (msg)), exit(1), __res_tmp.value.ok) \
            : __res_tmp.value.ok;                                                                               \
    })

#define expect_err(res, msg)                                                                                     \
    ({                                                                                                           \
        __auto_type __res_tmp = (res);                                                                           \
        (is_ok(__res_tmp))                                                                                       \
            ? (fprintf(stderr, "Panic at %s:%d: %s\n", __FILE__, __LINE__, (msg)), exit(1), __res_tmp.value.err) \
            : __res_tmp.value.err;                                                                               \
    })

#define unwrap_or(res, default_val)                            \
    ({                                                         \
        __auto_type __res_tmp = (res);                         \
        is_ok(__res_tmp) ? __res_tmp.value.ok : (default_val); \
    })

#define unwrap_or_else(res, func)                         \
    ({                                                    \
        __auto_type __res_tmp = (res);                    \
        is_ok(__res_tmp) ? __res_tmp.value.ok : (func)(); \
    })

#define __RESULT_MAP_PASTE(out_ok_type, err_type, res_in, var, ...)         \
    ({                                                                      \
        __auto_type __res_tmp = (res_in);                                   \
        (is_err(__res_tmp))                                                 \
            ? __RESULT_ERR_IMPL(out_ok_type, err_type, __res_tmp.value.err) \
            : ({                                                            \
                  typeof(__res_tmp.value.ok) var = __res_tmp.value.ok;      \
                  __RESULT_OK_IMPL(out_ok_type, err_type, __VA_ARGS__);     \
              });                                                           \
    })

#define __RESULT_MAP_IMPL(out_ok_type, err_type, res_in, var, ...) \
    __RESULT_MAP_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)

#define map(types, res_in, var, ...) \
    __RESULT_MAP_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

#define __RESULT_MAP_ERR_PASTE(ok_type, out_err_type, res_in, var, ...)   \
    ({                                                                    \
        __auto_type __res_tmp = (res_in);                                 \
        (is_ok(__res_tmp))                                                \
            ? __RESULT_OK_IMPL(ok_type, out_err_type, __res_tmp.value.ok) \
            : ({                                                          \
                  typeof(__res_tmp.value.err) var = __res_tmp.value.err;  \
                  __RESULT_ERR_IMPL(ok_type, out_err_type, __VA_ARGS__);  \
              });                                                         \
    })

#define __RESULT_MAP_ERR_IMPL(ok_type, out_err_type, res_in, var, ...) \
    __RESULT_MAP_ERR_PASTE(ok_type, out_err_type, res_in, var, __VA_ARGS__)

#define map_err(types, res_in, var, ...) \
    __RESULT_MAP_ERR_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

#define __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, ...)    \
    ({                                                                      \
        __auto_type __res_tmp = (res_in);                                   \
        (is_err(__res_tmp))                                                 \
            ? __RESULT_ERR_IMPL(out_ok_type, err_type, __res_tmp.value.err) \
            : ({                                                            \
                  typeof(__res_tmp.value.ok) var = __res_tmp.value.ok;      \
                  __VA_ARGS__                                               \
              });                                                           \
    })

#define __RESULT_AND_THEN_IMPL(out_ok_type, err_type, res_in, var, ...) \
    __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)

#define and_then(types, res_in, var, ...) \
    __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

Everything goes go well so for. But then I add:

 #define __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, ...)    \
    ({                                                                      \
        __auto_type __res_tmp = (res_in);                                   \
        (is_err(__res_tmp))                                                 \
            ? __RESULT_ERR_IMPL(out_ok_type, err_type, __res_tmp.value.err) \
            : ({                                                            \
                  typeof(__res_tmp.value.ok) var = __res_tmp.value.ok;      \
                  __VA_ARGS__                                               \
              });                                                           \
    })

#define __RESULT_AND_THEN_IMPL(out_ok_type, err_type, res_in, var, ...) \
    __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)

#define and_then(types, res_in, var, ...) \
    __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

I wrote a test to show the problem:

#include "result.h" // The header file with the macro definitions
#include <stdio.h>

// --- Type Definitions ---
typedef const char *CString;

// --- 1. Define the required Result type ---
// Result<int, CString>
DEFINE_RESULT(int, CString);

// --- Helper Functions for and_then ---

/**
 * @brief A function for the and_then test (Ok -> Ok or Ok -> Err)
 * Simulates an operation that might fail.
 */
Result_int_CString sophisticated_op(int i)
{
  printf("    -> (Called sophisticated_op(%d))\n", i);
  if (i > 10)
  {
    // Return a new Ok
    return Ok((int, CString), i * 2);
  }
  else
  {
    // Return an Err
    return Err((int, CString), "value too small");
  }
}

/**
 * @brief A function for the and_then test (Ok -> Err)
 */
Result_int_CString sophisticated_err_op(int i)
{
  printf("    -> (Called sophisticated_err_op(%d))\n", i);
  // Always returns Err
  return Err((int, CString), "sophisticated op failed");
}

// --- Main Test Function ---
int main()
{
  // --- Setup: Basic values ---
  // These macros (Ok, Err) work correctly.
  Result_int_CString res_ok = Ok((int, CString), 42);
  Result_int_CString res_err = Err((int, CString), "file not found");

  printf("\n--- 4. Chaining (and_then) ---\n");
  printf("Compilation is expected to fail on the following 3 lines:\n");

  // 4a. Ok(42) |> and_then(sophisticated_op) -> Ok(84)
  // COMPILATION FAILS HERE
  Result_int_CString chain1 = and_then(
      (int, CString),       // Target type (U, E)
      res_ok,               // Input Ok(42)
      val,                  // Bind variable
      sophisticated_op(val) // Expression -> Ok(84)
  );

  // 4b. Ok(42) |> and_then(sophisticated_err_op) -> Err("...")
  // COMPILATION FAILS HERE
  Result_int_CString chain2 = and_then(
      (int, CString), // Target type (U, E)
      res_ok,         // Input Ok(42)
      val,
      sophisticated_err_op(val) // Expression -> Err(...)
  );

  // 4c. Err(...) |> and_then(sophisticated_op) -> Err(...) (short-circuit)
  // COMPILATION FAILS HERE
  Result_int_CString chain3 = and_then(
      (int, CString),       // Target type (U, E)
      res_err,              // Input Err("file not found")
      val,                  // Bind variable
      sophisticated_op(val) // Expression - should not execute
  );

  printf("...If compilation somehow succeeded, printing results:\n");

  // These lines just print the results.
  // They require is_ok, unwrap_or, and expect_err from result.h.
  if (is_ok(chain1))
  {
    printf("and_then(Ok(42), op_ok): value = %d\n", unwrap_or(chain1, -1));
  }
  if (is_err(chain2))
  {
    printf("and_then(Ok(42), op_err): error = \"%s\"\n", expect_err(chain2, "should be err"));
  }
  if (is_err(chain3))
  {
    printf("and_then(Err, op_ok): error = \"%s\"\n", expect_err(chain3, "should be err"));
  }

  printf("All tests finished.\n");

  return 0;
}

I compiled it using clang and got this error:

karesis@Celestina:~/Projects/cprint$ clang -Wall -Wextra -std=c23 src/test_result.c -o test
src/test_result.c:141:31: error: expected ';' after expression
  141 |   Result_int_CString chain1 = and_then(
      |                               ^
src/result.h:134:5: note: expanded from macro 'and_then'
  134 |     __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))
      |     ^
src/result.h:131:63: note: expanded from macro '__RESULT_AND_THEN_IMPL'
  131 |     __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)
      |                                                               ^
src/test_result.c:150:31: error: expected ';' after expression
  150 |   Result_int_CString chain2 = and_then(
      |                               ^
src/result.h:134:5: note: expanded from macro 'and_then'
  134 |     __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))
      |     ^
src/result.h:131:63: note: expanded from macro '__RESULT_AND_THEN_IMPL'
  131 |     __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)
      |                                                               ^
src/test_result.c:159:31: error: expected ';' after expression
  159 |   Result_int_CString chain3 = and_then(
      |                               ^
src/result.h:134:5: note: expanded from macro 'and_then'
  134 |     __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))
      |     ^
src/result.h:131:63: note: expanded from macro '__RESULT_AND_THEN_IMPL'
  131 |     __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)
      |                                                               ^
3 errors generated.
karesis@Celestina:~/Projects/cprint$ 

I tried running just the preprocessor (clang -Wall -Wextra -std=c23 -E test_result.c > expanded.txt) and saw the "ghost ,":

...
  Result_int_CString chain1 = ({ __auto_type __res_tmp = (res_ok); (((__res_tmp).kind == ERR)) ? (Result_int_CString) { .kind = ERR, .value = {.err = __res_tmp.value.err } } : ({ typeof(__res_tmp.value.ok) val = __res_tmp.value.ok; (sophisticated_op(val)), }); });
# 64 "test_mre.c"
  Result_int_CString chain2 = ({ __auto_type __res_tmp = (res_ok); (((__res_tmp).kind == ERR)) ? (Result_int_CString) { .kind = ERR, .value = {.err = __res_tmp.value.err } } : ({ typeof(__res_tmp.value.ok) val = __res_tmp.value.ok; (sophisticated_err_op(val)), }); });
# 73 "test_mre.c"
  Result_int_CString chain3 = ({ __auto_type __res_tmp = (res_err); (((__res_tmp).kind == ERR)) ? (Result_int_CString) { .kind = ERR, .value = {.err = __res_tmp.value.err } } : ({ typeof(__res_tmp.value.ok) val = __res_tmp.value.ok; (sophisticated_op(val)), }); });
...

It's like (using chain1 as example):

Result_int_CString chain1 =
    ({
        __auto_type __res_tmp = (res_ok);
        (
            ((__res_tmp).kind == ERR)
        )
        ? (Result_int_CString){
            .kind = ERR,
            .value = {.err = __res_tmp.value.err}
          }
        : ({
            typeof(__res_tmp.value.ok) val = __res_tmp.value.ok;
            (sophisticated_op(val)), /* <--- Problematic Comma Here */
          });
    });

I have no idea why there's a comma there. It is supposed to be a ; so I try to add a ;:

#define __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, ...)    \
    ({                                                                      \
        __auto_type __res_tmp = (res_in);                                   \
        (is_err(__res_tmp))                                                 \
            ? __RESULT_ERR_IMPL(out_ok_type, err_type, __res_tmp.value.err) \
            : ({                                                            \
                  typeof(__res_tmp.value.ok) var = __res_tmp.value.ok;      \
                  __VA_ARGS__; /* <--- I add a `;` */                       \
              });                                                           \
    })

But another bug occurred:

karesis@Celestina:~/Projects/cprint$ clang -Wall -Wextra -std=c23 test_mre.c -o test
test_mre.c:55:31: error: expected expression
   55 |   Result_int_CString chain1 = and_then(
      |                               ^
./result.h:132:5: note: expanded from macro 'and_then'
  132 |     __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))
      |     ^
./result.h:129:5: note: expanded from macro '__RESULT_AND_THEN_IMPL'
  129 |     __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)
      |     ^
./result.h:124:30: note: expanded from macro '__RESULT_AND_THEN_PASTE'
  124 |                   __VA_ARGS__; /* <--- I add a `;` */                       \
      |                              ^
test_mre.c:64:31: error: expected expression
   64 |   Result_int_CString chain2 = and_then(
      |                               ^
./result.h:132:5: note: expanded from macro 'and_then'
  132 |     __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))
      |     ^
./result.h:129:5: note: expanded from macro '__RESULT_AND_THEN_IMPL'
  129 |     __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)
      |     ^
./result.h:124:30: note: expanded from macro '__RESULT_AND_THEN_PASTE'
  124 |                   __VA_ARGS__; /* <--- I add a `;` */                       \
      |                              ^
test_mre.c:73:31: error: expected expression
   73 |   Result_int_CString chain3 = and_then(
      |                               ^
./result.h:132:5: note: expanded from macro 'and_then'
  132 |     __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))
      |     ^
./result.h:129:5: note: expanded from macro '__RESULT_AND_THEN_IMPL'
  129 |     __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)
      |     ^
./result.h:124:30: note: expanded from macro '__RESULT_AND_THEN_PASTE'
  124 |                   __VA_ARGS__; /* <--- I add a `;` */                       \
      |                              ^
3 errors generated.
karesis@Celestina:~/Projects/cprint$ 

It seems that it actually needs a expression there. clang -E shows:

...
  Result_int_CString chain1 = ({ __auto_type __res_tmp = (res_ok); (((__res_tmp).kind == ERR)) ? (Result_int_CString) { .kind = ERR, .value = {.err = __res_tmp.value.err } } : ({ typeof(__res_tmp.value.ok) val = __res_tmp.value.ok; (sophisticated_op(val)),; }); });
# 64 "test_mre.c"
  Result_int_CString chain2 = ({ __auto_type __res_tmp = (res_ok); (((__res_tmp).kind == ERR)) ? (Result_int_CString) { .kind = ERR, .value = {.err = __res_tmp.value.err } } : ({ typeof(__res_tmp.value.ok) val = __res_tmp.value.ok; (sophisticated_err_op(val)),; }); });
# 73 "test_mre.c"
  Result_int_CString chain3 = ({ __auto_type __res_tmp = (res_err); (((__res_tmp).kind == ERR)) ? (Result_int_CString) { .kind = ERR, .value = {.err = __res_tmp.value.err } } : ({ typeof(__res_tmp.value.ok) val = __res_tmp.value.ok; (sophisticated_op(val)),; }); });
...

I checked my code again and again, but I found nothing wrong:

#pragma once

#include <stdio.h>
#include <stdlib.h>

#define __RESULT_EAT_PARENS(...) __VA_ARGS__

typedef enum ResultKind
{
    OK,
    ERR
} ResultKind;

#define DEFINE_RESULT(ok_type, err_type)         \
    typedef struct Result_##ok_type##_##err_type \
    {                                            \
        ResultKind kind;                         \
        union                                    \
        {                                        \
            ok_type ok;                          \
            err_type err;                        \
        } value;                                 \
    } Result_##ok_type##_##err_type;

#define __RESULT_OK_PASTE(ok_type, err_type, ...) \
    (Result_##ok_type##_##err_type)               \
    {                                             \
        .kind = OK, .value = {.ok = __VA_ARGS__ } \
    }

#define __RESULT_OK_IMPL(ok_type, err_type, ...) \
    __RESULT_OK_PASTE(ok_type, err_type, __VA_ARGS__)

#define __RESULT_ERR_PASTE(ok_type, err_type, ...)  \
    (Result_##ok_type##_##err_type)                 \
    {                                               \
        .kind = ERR, .value = {.err = __VA_ARGS__ } \
    }

#define __RESULT_ERR_IMPL(ok_type, err_type, ...) \
    __RESULT_ERR_PASTE(ok_type, err_type, __VA_ARGS__)

#define Ok(types, ...) \
    __RESULT_OK_IMPL(__RESULT_EAT_PARENS types, (__VA_ARGS__))

#define Err(types, ...) \
    __RESULT_ERR_IMPL(__RESULT_EAT_PARENS types, (__VA_ARGS__))

#define is_ok(res) \
    ((res).kind == OK)

#define is_err(res) \
    ((res).kind == ERR)

#define expect(res, msg)                                                                                        \
    ({                                                                                                          \
        __auto_type __res_tmp = (res);                                                                          \
        (is_err(__res_tmp))                                                                                     \
            ? (fprintf(stderr, "Panic at %s:%d: %s\n", __FILE__, __LINE__, (msg)), exit(1), __res_tmp.value.ok) \
            : __res_tmp.value.ok;                                                                               \
    })

#define expect_err(res, msg)                                                                                     \
    ({                                                                                                           \
        __auto_type __res_tmp = (res);                                                                           \
        (is_ok(__res_tmp))                                                                                       \
            ? (fprintf(stderr, "Panic at %s:%d: %s\n", __FILE__, __LINE__, (msg)), exit(1), __res_tmp.value.err) \
            : __res_tmp.value.err;                                                                               \
    })

#define unwrap_or(res, default_val)                            \
    ({                                                         \
        __auto_type __res_tmp = (res);                         \
        is_ok(__res_tmp) ? __res_tmp.value.ok : (default_val); \
    })

#define unwrap_or_else(res, func)                         \
    ({                                                    \
        __auto_type __res_tmp = (res);                    \
        is_ok(__res_tmp) ? __res_tmp.value.ok : (func)(); \
    })

#define __RESULT_MAP_PASTE(out_ok_type, err_type, res_in, var, ...)         \
    ({                                                                      \
        __auto_type __res_tmp = (res_in);                                   \
        (is_err(__res_tmp))                                                 \
            ? __RESULT_ERR_IMPL(out_ok_type, err_type, __res_tmp.value.err) \
            : ({                                                            \
                  typeof(__res_tmp.value.ok) var = __res_tmp.value.ok;      \
                  __RESULT_OK_IMPL(out_ok_type, err_type, __VA_ARGS__);     \
              });                                                           \
    })

#define __RESULT_MAP_IMPL(out_ok_type, err_type, res_in, var, ...) \
    __RESULT_MAP_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)

#define map(types, res_in, var, ...) \
    __RESULT_MAP_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

#define __RESULT_MAP_ERR_PASTE(ok_type, out_err_type, res_in, var, ...)   \
    ({                                                                    \
        __auto_type __res_tmp = (res_in);                                 \
        (is_ok(__res_tmp))                                                \
            ? __RESULT_OK_IMPL(ok_type, out_err_type, __res_tmp.value.ok) \
            : ({                                                          \
                  typeof(__res_tmp.value.err) var = __res_tmp.value.err;  \
                  __RESULT_ERR_IMPL(ok_type, out_err_type, __VA_ARGS__);  \
              });                                                         \
    })

#define __RESULT_MAP_ERR_IMPL(ok_type, out_err_type, res_in, var, ...) \
    __RESULT_MAP_ERR_PASTE(ok_type, out_err_type, res_in, var, __VA_ARGS__)

#define map_err(types, res_in, var, ...) \
    __RESULT_MAP_ERR_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

#define __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, ...)    \
    ({                                                                      \
        __auto_type __res_tmp = (res_in);                                   \
        (is_err(__res_tmp))                                                 \
            ? __RESULT_ERR_IMPL(out_ok_type, err_type, __res_tmp.value.err) \
            : ({                                                            \
                  typeof(__res_tmp.value.ok) var = __res_tmp.value.ok;      \
                  __VA_ARGS__; /* <--- I add a `;` */                       \
              });                                                           \
    })

#define __RESULT_AND_THEN_IMPL(out_ok_type, err_type, res_in, var, ...) \
    __RESULT_AND_THEN_PASTE(out_ok_type, err_type, res_in, var, __VA_ARGS__)

#define and_then(types, res_in, var, ...) \
    __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))karesis@Celestina:~/Projects/cprint$ 

Is this a weirdfeature of clang? (add , after a VAR__ARGS) or have I made a mistake? I would be extremely grateful for any help or insights!

MRE:

#include <stdio.h>

#define __RESULT_EAT_PARENS(...) __VA_ARGS__

#define PRINT_ARGS_MACRO(out_ok_type, err_type, res_in, var, ...) \
  printf("  arg 1 (out_ok_type): %s\n", #out_ok_type);            \
  printf("  arg 2 (err_type):    %s\n", #err_type);               \
  printf("  arg 3 (res_in):      %s\n", #res_in);                 \
  printf("  arg 4 (var):         %s\n", #var);                    \
  printf("  arg 5 (VA_ARGS):     %s\n", #__VA_ARGS__)

#define and_then_buggy(types, res_in, var, ...) \
  PRINT_ARGS_MACRO(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

int main()
{
  printf("--- use macro ---\n");
  and_then_buggy(
      (int, CString), // types
      "res_ok",       // res_in
      "val",          // var
      "my_func(val)"  // ...
  );

  printf("\n--- expected ---\n");
  printf("  arg 1 (out_ok_type): int\n");
  printf("  arg 2 (err_type):    CString\n");
  printf("  arg 3 (res_in):      \"res_ok\"\n");
  printf("  arg 4 (var):         \"val\"\n");
  printf("  arg 5 (VA_ARGS):     (\"my_func(val)\")\n");

  return 0;
}
4
  • There's a lot more code there than I would expect in a minimal reproducible example. Can't you reduce it at all? Commented Nov 6 at 7:34
  • No problem! here is the mre: Commented Nov 6 at 8:30
  • Or you can see it below (3CEZV also posted one in the answer) Commented Nov 6 at 8:35
  • Note that identifiers that start with __ are reserved for use by the implementation: "All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use." Commented Nov 6 at 19:17

1 Answer 1

4

__VA_ARGS__ is empty, because types is expanded to the value (int, CString) and is eaten by out_ok_type in __RESULT_AND_THEN_IMPL. This happens because only a literal like int, CString is split into two values, but not a value containing a comma. __RESULT_EAT_PARENS does not help, because parenthesis are a part of a value in types and they do not invoke the macro with parameters. You would see it if you test the top macros individually.

It's unclear why and_then is defined with 3 explicit parameters, and then use pass (int, CString). and_then should be defined with 4 explicit parameters.

Simple test

#define __RESULT_EAT_PARENS(...) __VA_ARGS__

#define __RESULT_AND_THEN_IMPL(out_ok_type, err_type, res_in, var, ...) \
  printf("%s\n", #out_ok_type);                                         \
  printf("%s\n", #err_type);                                            \
  printf("%s\n", #res_in);                                              \
  printf("%s\n", #var);                                                 \
  printf("%s\n", #__VA_ARGS__)

#define and_then(types, res_in, var, ...) \
  __RESULT_AND_THEN_IMPL(__RESULT_EAT_PARENS types, res_in, var, (__VA_ARGS__))

int main() {
  and_then((int, CString),            // Target type (U, E)
           res_err,                   // Input Err("file not found")
           val,                       // Bind variable
           sophisticated_err_op(val)  // Expression -> Err(...)
  );
}

outputs

__RESULT_EAT_PARENS (int, CString)
res_err
val
(sophisticated_err_op(val))

that is not what you want.

You might want __RESULT_AND_THEN_IMPL with 3 explicit parameters.

#define __RESULT_AND_THEN_IMPL(types, res_in, var, ...) \
  __RESULT_AND_THEN_PASTE(types, res_in, var, __VA_ARGS__)
Sign up to request clarification or add additional context in comments.

1 Comment

thats it! I tried a fix and it worked now: ...(remains)... #define RESULT_AND_THEN_CALL(...) _RESULT_AND_THEN_IMPL(_VA_ARGS) #define and_then(types, res_in, var, ...) \ RESULT_AND_THEN_CALL(_RESULT_EAT_PARENS types, res_in, var, (_VA_ARGS)) just delay it. very much thankful!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.