3

I have this ProcessStasts.h file that is included into two other .h files.

#pragma once

#include <mpi.h>
#include <cstddef>

struct ProcessStats
{
    int rank,
    itLeft,
    crtIt,
    processFlag;
    float speed;
};

MPI_Datatype MPI_Cust_ProcessStats_create()
{
    // set data to create new MPI data type
    MPI_Datatype MPI_Cust_ProcessStats;
    MPI_Datatype dataTypes[5] = {MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_FLOAT};
    int blockLengths[5] = {1, 1, 1, 1, 1};
    MPI_Aint offsets[5];

    offsets[0] = (MPI_Aint) offsetof(ProcessStats, rank);
    offsets[1] = (MPI_Aint) offsetof(ProcessStats, itLeft);
    offsets[2] = (MPI_Aint) offsetof(ProcessStats, crtIt);
    offsets[3] = (MPI_Aint) offsetof(ProcessStats, processFlag);
    offsets[4] = (MPI_Aint) offsetof(ProcessStats, speed);

    // create new MPI type based on data from above
    MPI_Type_create_struct(5, blockLengths, offsets, dataTypes, &MPI_Cust_ProcessStats);
    MPI_Type_commit(&MPI_Cust_ProcessStats);

    return MPI_Cust_ProcessStats;
}

When I try compiling I get this error: error LNK2005: MPI_Cust_ProcessStats_create(void) already defined. If I comment the #include "ProcessStasts.h" directive and the line that uses the ProcessStats struct, from one of the files, it compiles correctly. I even tryed to comment all lines dependent in ProcessStats and only leave the #include "ProcessStasts.h" statements and I get this lnk error. What is wrong?

2
  • Why is the first argument to the MPI structure datatype constructor 3 when you have 5 fields in the struct? Commented Nov 22, 2013 at 10:29
  • Because of an error, changed the struct with more vars... same as DataType array[6]. Thanks, that made me see something I would have discovered as a bug later. Commented Nov 22, 2013 at 10:51

2 Answers 2

4

You can write like this: first is ProcessStasts.h

#pragma once

#include <mpi.h>
#include <cstddef>

struct ProcessStats
{
    int rank,
    itLeft,
    crtIt,
    processFlag;
    float speed;
};

MPI_Datatype MPI_Cust_ProcessStats_create();

then is ProcessStasts.c

#include "ProcessStats.h"
MPI_Datatype MPI_Cust_ProcessStats_create()
{
    // set data to create new MPI data type
    MPI_Datatype MPI_Cust_ProcessStats;
    MPI_Datatype dataTypes[6] = {MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_FLOAT};
    int blockLengths[5] = {1, 1, 1, 1, 1};
    MPI_Aint offsets[5];

    offsets[0] = (MPI_Aint) offsetof(ProcessStats, rank);
    offsets[1] = (MPI_Aint) offsetof(ProcessStats, itLeft);
    offsets[2] = (MPI_Aint) offsetof(ProcessStats, crtIt);
    offsets[3] = (MPI_Aint) offsetof(ProcessStats, processFlag);
    offsets[4] = (MPI_Aint) offsetof(ProcessStats, speed);

    // create new MPI type based on data from above
    MPI_Type_create_struct(3, blockLengths, offsets, dataTypes, &MPI_Cust_ProcessStats);
    MPI_Type_commit(&MPI_Cust_ProcessStats);

    return MPI_Cust_ProcessStats;
}

Then you can include the ProcessStasts.h as many times as you want. As a suggestion, do not define functions in a head file.

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, that solved the problem. However I don't understand why, since #pragma once is there
#pragma once is helped when you compile one source file. But for different source file it will also do the include operation when compiling.
2

#pragma once instructs the preprocessor not to include a header file twice. This is mostly used to prevent recursive inclusions and multiple indirect inclusions, e.g.:

#include <a.h>  // a.h already includes b.h
#include <b.h>

Without #pragma once at the beginning of b.h, its content will get included twice and possibly lead to redefinition of some symbols.

What happens in your case is an entirely different matter. By default functions in C and C++ have external linkage. It means that if you have function foo() defined in file bar.c and then you compile bar.c to the object file bar.o, the object file exports a global symbol by the name of foo (actually C++ will decorate the name in order to support overloading), which symbol can be accessed (referred to) from other object files. Now if file baz.c contains the definition of another function foo() (which in the case of C++ has the same signature), the object file baz.o also exports a global symbol by the name of foo. When the object files get linked together in order to produce an executable file, the linker tries to resolve each symbol to a unique memory address. But now there is a problem: there are two symbols foo and they both have different addresses. The linker is (usually) not a psychic, so it simply gives you an error message about symbol redefinition and terminates.

Both C and C++ provide a mechanism to control the linkage of functions. If you add the static keyword, the function symbol is no longer global and become only visible to the code that shares the same unit of compilation. Such functions have static linkage. That's why functions that are defined in header files almost always come with the static keyword:

#pragma once

#include <mpi.h>
#include <cstddef>

struct ProcessStats
{
    int rank,
    itLeft,
    crtIt,
    processFlag;
    float speed;
};

static MPI_Datatype MPI_Cust_ProcessStats_create()
{
    ...
}

Now MPI_Cust_ProcessStats_create() is only going to be visible in the source file that includes the header file.

Unsolicited advice: The MPI_ prefix is reserved for MPI API calls. Using it for user functions is a bad programming practice since there are tools that rely on the fact that only MPI calls begin with MPI_ and might get confused.

5 Comments

How would you approach this, as moving funct to .cpp file or by adding static, and why (how does one mode vs the other benefit)?
It is a matter of preference. If you put it in a separate source file, it is only compiled once. If you keep it in the header, it is compiled many times. If you keep it in the header file and have to make changes to the code of the function, you would have to recompile every file that includes the header file. If it is in a separate file, only it has to be recompiled.
Some people prefer to keep small functions in header files since these can then be inlined by the optimising compilers. If the function is in a separate translation unit, then it becomes harder (but not impossible with things like inter-procedural optimisations).
I found this mode of creating MPI_Datatype on stackoverflow. How can I create a datatype using this function?
This is the better answer as it explains the source of the error; it's about linkage. Note that declaring the function inline would work as well.

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.