Home > Net >  Why does code break on exit when adding a conditional for calling the #define function
Why does code break on exit when adding a conditional for calling the #define function

Time:03-04

My problem is I am trying to add a conditional if-else statement.
The whole program works fine with the original code, but the code breaks when I add it.
My first question is why is it breaking?
My second is how can I add the conditional without breaking it?

Orignal Code

static PyObject*
Aligner_gotoh_local_score_compare(Aligner* self,
                                  const int* sA, Py_ssize_t nA,
                                  const int* sB, Py_ssize_t nB)
{
    const double match = self->match;
    const double mismatch = self->mismatch;
    const double mismatch2 = self->mismatch2;
    const double mismatch3 = self->mismatch3;
    const int wildcard = self->wildcard;

    GOTOH_LOCAL_SCORE(COMPARE_SCORE);
}

Changed Code

static PyObject*
Aligner_gotoh_local_score_compare(Aligner* self,
                                  const int* sA, Py_ssize_t nA,
                                  const int* sB, Py_ssize_t nB)
{
    const double match = self->match;
    const double mismatch = self->mismatch;
    const double mismatch2 = self->mismatch2;
    const double mismatch3 = self->mismatch3;
    const int wildcard = self->wildcard;

    if (mismatch2 == 0)
    {
        GOTOH_LOCAL_SCORE(COMPARE_SCORE);
    }
    else
    {
        GOTOH_LOCAL_SCORE(COMPARE_MULTI_SCORES); 
    }
}

Error messages

Bio/Align/_aligners.c: In function ‘Aligner_gotoh_local_score_compare’:
Bio/Align/_aligners.c:4937:1: error: duplicate label ‘exit’
 4937 | exit: \
      | ^~~~
Bio/Align/_aligners.c:6298:9: note: in expansion of macro ‘GOTOH_LOCAL_SCORE’
 6298 |         GOTOH_LOCAL_SCORE(COMPARE_MULTI_SCORES);
      |         ^~~~~~~~~~~~~~~~~
Bio/Align/_aligners.c:4937:1: note: previous definition of ‘exit’ was here
 4937 | exit: \
      | ^~~~

Function where code breaks

#define GOTOH_LOCAL_SCORE(align_score) \
    int i; \
    int j; \
    int kA; \
    int kB; \
    const double gap_open_A = self->target_internal_open_gap_score; \
    const double gap_open_B = self->query_internal_open_gap_score; \
    const double gap_extend_A = self->target_internal_extend_gap_score; \
    const double gap_extend_B = self->query_internal_extend_gap_score; \
    double* M_row = NULL; \
    double* Ix_row = NULL; \
    double* Iy_row = NULL; \
    double score; \
    double temp; \
    double M_temp; \
    double Ix_temp; \
    double Iy_temp; \
    double maximum = 0.0; \
\
    /* Gotoh algorithm with three states */ \
    M_row = PyMem_Malloc((nB 1)*sizeof(double)); \
    if (!M_row) goto exit; \
    Ix_row = PyMem_Malloc((nB 1)*sizeof(double)); \
    if (!Ix_row) goto exit; \
    Iy_row = PyMem_Malloc((nB 1)*sizeof(double)); \
    if (!Iy_row) goto exit; \
 \
    /* The top row of the score matrix is a special case, \
     * as there are no previously aligned characters. \
     */ \
    M_row[0] = 0; \
    Ix_row[0] = -DBL_MAX; \
    Iy_row[0] = -DBL_MAX; \
    for (j = 1; j <= nB; j  ) { \
        M_row[j] = -DBL_MAX; \
        Ix_row[j] = -DBL_MAX; \
        Iy_row[j] = 0; \
    } \
    for (i = 1; i < nA; i  ) { \
        M_temp = M_row[0]; \
        Ix_temp = Ix_row[0]; \
        Iy_temp = Iy_row[0]; \
        M_row[0] = -DBL_MAX; \
        Ix_row[0] = 0; \
        Iy_row[0] = -DBL_MAX; \
        kA = sA[i-1]; \
        for (j = 1; j < nB; j  ) { \
            kB = sB[j-1]; \
            SELECT_SCORE_GOTOH_LOCAL_ALIGN(M_temp, \
                                           Ix_temp, \
                                           Iy_temp, \
                                           (align_score)); \
            M_temp = M_row[j]; \
            M_row[j] = score; \
            SELECT_SCORE_LOCAL3(M_temp   gap_open_B, \
                                Ix_row[j]   gap_extend_B, \
                                Iy_row[j]   gap_open_B); \
            Ix_temp = Ix_row[j]; \
            Ix_row[j] = score; \
            SELECT_SCORE_LOCAL3(M_row[j-1]   gap_open_A, \
                                Ix_row[j-1]   gap_open_A, \
                                Iy_row[j-1]   gap_extend_A); \
            Iy_temp = Iy_row[j]; \
            Iy_row[j] = score; \
        } \
        kB = sB[nB-1]; \
        Ix_row[nB] = 0; \
        Iy_row[nB] = 0; \
        SELECT_SCORE_GOTOH_LOCAL_ALIGN(M_temp, \
                                       Ix_temp, \
                                       Iy_temp, \
                                       (align_score)); \
        M_temp = M_row[nB]; \
        M_row[nB] = score; \
    } \
    M_temp = M_row[0]; \
    Ix_temp = Ix_row[0]; \
    Iy_temp = Iy_row[0]; \
    M_row[0] = -DBL_MAX; \
    Ix_row[0] = 0; \
    Iy_row[0] = -DBL_MAX; \
    kA = sA[nA-1]; \
    for (j = 1; j < nB; j  ) { \
        kB = sB[j-1]; \
        SELECT_SCORE_GOTOH_LOCAL_ALIGN(M_temp, \
                                       Ix_temp, \
                                       Iy_temp, \
                                       (align_score)); \
        M_temp = M_row[j]; \
        M_row[j] = score; \
        Ix_temp = Ix_row[j]; \
        Iy_temp = Iy_row[j]; \
        Ix_row[j] = 0; \
        Iy_row[j] = 0; \
    } \
    kB = sB[nB-1]; \
    SELECT_SCORE_GOTOH_LOCAL_ALIGN(M_temp, \
                                   Ix_temp, \
                                   Iy_temp, \
                                   (align_score)); \
    PyMem_Free(M_row); \
    PyMem_Free(Ix_row); \
    PyMem_Free(Iy_row); \
    return PyFloat_FromDouble(maximum); \
exit: \                                  // Breaks here <<<<<<<
    if (M_row) PyMem_Free(M_row); \
    if (Ix_row) PyMem_Free(Ix_row); \
    if (Iy_row) PyMem_Free(Iy_row); \
    return PyErr_NoMemory(); \

Images for more context:
Original code
Changed code
Errors
Where code breaks

CodePudding user response:

There is nothing magic going on here, you need to understand what macros do.

when you do this

if (mismatch2 == 0)
{
    GOTOH_LOCAL_SCORE(COMPARE_SCORE);
}
else
{
    GOTOH_LOCAL_SCORE(COMPARE_MULTI_SCORES); 
}

the line GOTOH_LOCAL_SCORE(COMPARE_SCORE); is replaced by

    int i; \
    int j; \
    int kA; \
    int kB; \
    ....
    .....
    PyMem_Free(M_row); \
    PyMem_Free(Ix_row); \
    PyMem_Free(Iy_row); \
    return PyFloat_FromDouble(maximum); \
exit: \                                  // Breaks here <<<<<<<
    if (M_row) PyMem_Free(M_row); \
    if (Ix_row) PyMem_Free(Ix_row); \
    if (Iy_row) PyMem_Free(Iy_row); \
    return PyErr_NoMemory(); \

then GOTOH_LOCAL_SCORE(COMPARE_MULTI_SCORES); is also replaced by

    int i; \
    int j; \
    int kA; \
    int kB; \
    ....
    .....
    PyMem_Free(M_row); \
    PyMem_Free(Ix_row); \
    PyMem_Free(Iy_row); \
    return PyFloat_FromDouble(maximum); \
exit: \                                  // Breaks here <<<<<<<
    if (M_row) PyMem_Free(M_row); \
    if (Ix_row) PyMem_Free(Ix_row); \
    if (Iy_row) PyMem_Free(Iy_row); \
    return PyErr_NoMemory(); \

so now you have 2 exit: labels declared, thats not allowed.

As others have said, just make it a function

CodePudding user response:

As diagnosed in the comments to the question, the definition of the macro GOTOH_LOCAL_SCORE defines the label exit: — consequently, you cannot use the macro twice in a single function. That's because ordinary labels must be unique inside a function. However, case labels and default labels can be repeated in different switch statements.

Check where else that macro is used — but if it was just that one function (which seems likely), then … well, it is a ghastly huge macro and shouldn't be a macro.

Intrusive option — split macro into pieces

It might be feasible to split it into three macros:

  • GOTOH_LOCAL_SCORE_DECLARATIONS — defining the variables once.
  • GOTOH_LOCAL_SCORE_WITHOUT_EXIT containing all the material up to but not including the exit: label, and
  • GOTOH_LOCAL_SCORE_EXIT containing the exit: label and following lines.

You can then use the _DECLARATIONS macro once, the _WITHOUT_EXIT variant twice and add the _EXIT variant once. You can define the original macro using these so you don't break anywhere else that it is used.

In outline:

#define GOTOH_LOCAL_SCORE_DECLARATIONS() \
    int i; \
    int j; \
    int kA; \
    int kB; \
    const double gap_open_A = self->target_internal_open_gap_score; \
    const double gap_open_B = self->query_internal_open_gap_score; \
    const double gap_extend_A = self->target_internal_extend_gap_score; \
    const double gap_extend_B = self->query_internal_extend_gap_score; \
    double* M_row = NULL; \
    double* Ix_row = NULL; \
    double* Iy_row = NULL; \
    double score; \
    double temp; \
    double M_temp; \
    double Ix_temp; \
    double Iy_temp; \
    double maximum = 0.0


#define GOTOH_LOCAL_SCORE_WITHOUT_EXIT(align_score) \
    /* Gotoh algorithm with three states */ \
    M_row = PyMem_Malloc((nB 1)*sizeof(double)); \
    if (!M_row) goto exit; \
    Ix_row = PyMem_Malloc((nB 1)*sizeof(double)); \
    if (!Ix_row) goto exit; \
    Iy_row = PyMem_Malloc((nB 1)*sizeof(double)); \
    if (!Iy_row) goto exit; \
                                   \
    …                              \
                                   \
    PyMem_Free(M_row); \
    PyMem_Free(Ix_row); \
    PyMem_Free(Iy_row); \
    return PyFloat_FromDouble(maximum); \

#define GOTOH_LOCAL_SCORE_EXIT() \
exit: \
    if (M_row) PyMem_Free(M_row); \
    if (Ix_row) PyMem_Free(Ix_row); \
    if (Iy_row) PyMem_Free(Iy_row); \
    return PyErr_NoMemory()

#define GOTOH_LOCAL_SCORE(align_score) \
    GOTOH_LOCAL_SCORE_DECLARATIONS(); \
    GOTOH_LOCAL_SCORE_WITHOUT_EXIT(align_score); \
    GOTOH_LOCAL_SCORE_EXIT();

And then use these macros like this:

static PyObject*
Aligner_gotoh_local_score_compare(Aligner* self,
                                  const int* sA, Py_ssize_t nA,
                                  const int* sB, Py_ssize_t nB)
{
    const double match = self->match;
    const double mismatch = self->mismatch;
    const double mismatch2 = self->mismatch2;
    const double mismatch3 = self->mismatch3;
    const int wildcard = self->wildcard;
    GOTOH_LOCAL_SCORE_DECLARATIONS();

    if (mismatch2 == 0)
    {
        GOTOH_LOCAL_SCORE_WITHOUT_EXIT(COMPARE_SCORE);
    }
    else
    {
        GOTOH_LOCAL_SCORE_WITHOUT_EXIT(COMPARE_MULTI_SCORES); 
    }

    GOTOH_LOCAL_SCORE_EXIT();
}

Less intrusive alternative

static PyObject*
Aligner_gotoh_local_score_compare_VariantA(Aligner* self,
                                  const int* sA, Py_ssize_t nA,
                                  const int* sB, Py_ssize_t nB)
{
    const double match = self->match;
    const double mismatch = self->mismatch;
    const double mismatch2 = self->mismatch2;
    const double mismatch3 = self->mismatch3;
    const int wildcard = self->wildcard;

    GOTOH_LOCAL_SCORE(COMPARE_SCORE);
}

static PyObject*
Aligner_gotoh_local_score_compare_VariantB(Aligner* self,
                                  const int* sA, Py_ssize_t nA,
                                  const int* sB, Py_ssize_t nB)
{
    const double match = self->match;
    const double mismatch = self->mismatch;
    const double mismatch2 = self->mismatch2;
    const double mismatch3 = self->mismatch3;
    const int wildcard = self->wildcard;

    GOTOH_LOCAL_SCORE(COMPARE_MULTI_SCORE);
}
 

static PyObject*
Aligner_gotoh_local_score_compare(Aligner* self,
                                  const int* sA, Py_ssize_t nA,
                                  const int* sB, Py_ssize_t nB)   
{
    if (self->mismatch2 == 0)
    {
        return Aligner_gotoh_local_score_compare_VariantA(self, sA, nA, sB, nB);
    }
    else
    {
        return Aligner_gotoh_local_score_compare_VariantB(self, sA, nA, sB, nB);
    }
}

That leads to some fairly horrendous code duplication, but doesn't require any change to the macro(s) provided by other people.

  • Related