0

How can I make the function below constexpr?

This is a function that I use to create a POD struct that is defined in a "C" header file that I cannot change.

I ended up with the following static function helper (which works) to make these structs, I believe it is not constexpr, as the strncpy call violates constexpr.

static auto makeWPTEntry(
    const char* name,
    double aLatDeg, double aLatMin, double aLonDeg,
    double aLonMin, char bSeven = false,
    double aCourse = 0, double aAzimuth = 0, double aDist = 0,
    int aETASecs = 0, int aMod = 0)->WPTEntry {
    auto result = WPTEntry{
        bSeven,
        {},
        (aLatDeg + aLatMin) / 60.0,
        (aLonDeg + aLonMin) / 60.0,
        aCourse, aAzimuth,
        aDist, aETASecs, aMod
    };
    strncpy(result.name, name, sizeof(result.name));
    return result;
}

I an struggling to make something along the following lines (which is constexpr) - however I really need the non constexpr function signature with a const char* name parameter, Unfortunately, I don't know how to inline convert this const char* into a fixed sized array char const (&name)[SIZ_WAYPOINT_NAME] to be compatible with the struct field's constructor.

constexpr auto makeWPTEntry(
    char const (&name)[SIZ_WAYPOINT_NAME],
    double aLatDeg, double aLatMin, double aLonDeg,
    double aLonMin, char bSeven = false,
    double aCourse = 0, double aAzimuth = 0,
    double aDist = 0,
    int aETASecs = 0, int aMod = 0)->WPTEntry {
    auto result = WPTEntry{
        bSeven,
        name,
        (aLatDeg + aLatMin) / 60.0,
        (aLonDeg + aLonMin) / 60.0,
        aCourse, aAzimuth,
        aDist, aETASecs, aMod
    };
    //strncpy(result.name, name, sizeof(result.name));
    return result;
}

The WPTEntry is a simple external C data structure.

#define SIZ_WAYPOINT_NAME 9

typedef struct {
    char    seven;
    char    name[SIZ_WAYPOINT_NAME];
    double  lat;        
    double  lon;        
    double  crs;        
    double  az2;        
    double  distance;   
    int     eta_secs;  
    int     mod;
} WPTEntry;

The way I create these structures is as follows:

const auto wpt = makeWPTEntry("", -24.499, 0, -81.501, 0);

I asked something similar question before, however I never received an answer.

I found the following from this site but I am not sure how I might be able to use it to adapt my parameter, perhaps its might be useful of not.

struct ArrayWrapper
{
   char const *address_;
   size_t      length_;

   template<int N>
   ArrayWrapper(char const (&array)[N])
   {
      address = &array[0];
      length_ = N;
   }
};
johnco3
  • 2,401
  • 4
  • 35
  • 67
  • Would be a duplicate of https://stackoverflow.com/questions/54382238/c11-initialize-constexpr-char-array-with-another-constexpr-char-array ? – KamilCuk Jun 20 '20 at 21:19
  • @KamilCuk I forgot to mention that in my c++ client code (the one that contains the makeWPTEntry - I do not have acccess to stl code (this is special stripped down GCC without stl). The index sequence looks very complicated but also promising but also a non starter given the inability to use stl. I should have mentioned that in the question – johnco3 Jun 20 '20 at 21:37
  • `very complicated` it's just overloaded sequence for number of template arguments... [here you go](https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/utility#L325) ~20 lines to copy. – KamilCuk Jun 20 '20 at 21:51
  • Tried to compile the 20 lines of code in Visual Studio without including STL headers) and got an error associated with __integer_pack(_Num)... error C3546: '...': there are no parameter packs available to expand - any ideas, I think having access to the make index sequences would be really useful. I'm just starting to understand how powerful this feature could be – johnco3 Jun 22 '20 at 16:54
  • `__integer_pack` is a gcc extension. I have no experience with microsoft. – KamilCuk Jun 22 '20 at 17:09
  • @KamilCuk digging around in visual studio's looks like they use a built in type 'using make_integer_sequence = __make_integer_seq;' to facilitate generating sequences. – johnco3 Jun 22 '20 at 20:28

1 Answers1

1

A bit of twiddling around std::integer_sequence and I got it:

#include <utility>
#include <iostream>

extern "C" {
    #define SIZ_WAYPOINT_NAME 9

typedef struct {
    char    seven;
    char    name[SIZ_WAYPOINT_NAME];
    double  lat;        
    double  lon;        
    double  crs;        
    double  az2;        
    double  distance;   
    int     eta_secs;  
    int     mod;
} WPTEntry;

};

template<std::size_t N, std::size_t... I>
constexpr WPTEntry makeWPTEntry_in(
        char const (&name)[N],
        double aLatDeg, double aLatMin, double aLonDeg,
        double aLonMin, char bSeven,
        double aCourse, double aAzimuth,
        double aDist,
        int aETASecs, int aMod,
        std::index_sequence<I...>) {
    return WPTEntry{
        bSeven,
        { name[I]... },
        (aLatDeg + aLatMin) / 60.0,
        (aLonDeg + aLonMin) / 60.0,
        aCourse, aAzimuth,
        aDist, aETASecs, aMod
    };
}

template<std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr WPTEntry makeWPTEntry(
            char const (&name)[N],
            double aLatDeg, double aLatMin, double aLonDeg,
            double aLonMin, char bSeven = false,
            double aCourse = 0, double aAzimuth = 0,
            double aDist = 0,
            int aETASecs = 0, int aMod = 0) {
    return makeWPTEntry_in(name, 
        aLatDeg, aLatMin, aLonDeg,
        aLonMin, bSeven,
        aCourse, aAzimuth,
        aDist,
        aETASecs, aMod,
        Indices{});
}

int main() {
    const auto wpt = makeWPTEntry("", -24.499, 0, -81.501, 0);
}

But really, be a man and copy it:

template<std::size_t N>
constexpr WPTEntry makeWPTEntry(
            char const (&name)[N],
            double aLatDeg, double aLatMin, double aLonDeg,
            double aLonMin, char bSeven = false,
            double aCourse = 0, double aAzimuth = 0,
            double aDist = 0,
            int aETASecs = 0, int aMod = 0) {
    auto r = WPTEntry{
        bSeven,
        { 0 },
        (aLatDeg + aLatMin) / 60.0,
        (aLonDeg + aLonMin) / 60.0,
        aCourse, aAzimuth,
        aDist, aETASecs, aMod
    };
    for (size_t i = 0; i < N; ++i) {
        r.name[i] = name[i];
    }
    return r;
}

int main() {
    constexpr auto wpt = makeWPTEntry("abc", -24.499, 0, -81.501, 0);
    std::cout << wpt.name;
}
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Thanks, I used the 2nd version with the simple copy as you pointed out. I wish I knew a little more about index sequences - presumably I can avoid using STL by just copying lines 235-254 from the 'very complicated' link comment you sent earlier. From a curiosity perspective, In the indices version, could you explain in simple terms how it works I'm finding it difficult to follow how it works with the 2 template functions and one calling makeWPTEntry_in. I think it would be super useful to understand how it works. Thanks – johnco3 Jun 21 '20 at 03:45
  • While trying to understand the `index_sequence approach` you suggested, I cannot figure out why there is a need for a double dispatch through `makeWPTEntry_in`. Would fold expressions help here? I want to use these new techniques to initialize fixed constexpr arrays - both 2 dimensional and 1 dimensional - the latter you already solved for me. Also, how does the first const char* parameter to makeWptEntry("abc") bind itself to the fixed size `char const (&name)[N]`? Does the compiler make N=3 in this case and N=0 when using an empty string? Thanks – johnco3 Jun 30 '20 at 18:47
  • `"abc"` is an array of __4__ characters, and yes, the compiler [_deduces template parameter_](https://en.cppreference.com/w/cpp/language/template_argument_deduction). `Would fold expressions help here?` yea, I think you could make `makeWPTEntry` an variadic template with variadic argument pack with perfect forwarding, that would simplify the code. `I cannot figure out why` I think that question is too smart for me : ) – KamilCuk Jun 30 '20 at 19:00
  • Many thanks, I just figured out through static_assert that the compiler instantiates different instances based on the size of the parameter being passed in (I think). http://coliru.stacked-crooked.com/a/53c361a75ef066cb, can you point to an example of an variadic template with variadic argument pack with perfect forwarding. I think this is along the lines but I am not sure if index sequences come into play `template T create(Args&& ... args){ return T(std::forward(args)...); }` – johnco3 Jun 30 '20 at 19:13