Search code examples
c++segmentation-faultc++17variadic-templatesvariadic

How do I pass different data types in a variadic function, with parameter folds?


Edit: Reformatting the entire question

Making a Linked List lib, and I wanted to pass through multiple values in order to make the appending of items easier, so I created some Variadic Functions, which while testing with int values, worked well, but not for other data types.

My Linked List Code:

#include<bits/stdc++.h>
using namespace std;

template<typename data_type>
struct _node {
    data_type data{};
    _node<data_type>* link = nullptr;
    _node(data_type value) : data(value) {}
};

template<typename s_data_type>
struct linked_list {
private:
    typedef _node<s_data_type> node;
    typedef s_data_type dt;
    int _size{};
    node* head = nullptr;
    node* last = nullptr;
public:
    template<typename ...Args> linked_list(Args ...arg) {
        (append(arg), ...); 
    }
    template<typename ...Args> void append(Args ...arg) {
        (append(arg), ...);
    }

    void append(const dt value) {
        if (this->head == nullptr) {
            head = new node(value);
            last = head;
        }
        else {
            last->link = new node(value);
            last = last->link;
        }
        _size++;
    }
    //Methods, Constructors, etc...

};
int main(){
    linked_list<string> list("123", "Such a", "Why does this work");
    list.print();
    list.append("hmm");
    list.append("wooowowow", "Please Work");
    list.append("No");
    println();
    list.print();
    return 0;
}

Now, in order to append the list, I have a append function,which works fine by itself for tested data types Float, Int, String, and also the variadic functions as long as the intiallized LL is of int type.

The first variadic constructor, works well, doesn't throw any Segmentation errors, it's the overloaded append func which throws Segmentation faults, but it all works for Int type, which is because int is a structural class type for templates I guess? Probably not.

If Main function is written for linked_list<int>:

linked_list<int> list(1, 2, 3);
list.append(4, 5);
list.print();

Output:

{ 1 2 3 4 5 }

But for linked_list<string>, the variadic function append gives me a Segmentation Fault, which even after looking into it, I can't pinpoint why? Segmentation Fault

Exception has occurred.: Segmentation fault

Now I have looked into parameter folds, unary, binary stuff with folding parameters, read some docs and forums, and I came up with the following code, which I can barely understand why it works.

template<typename ...args> void append(dt val, args ...arg) {
    (append(val), ((arg), ...));
}

And for the String LL initialization it produces:

{ 123 Such a Why does this work }
{ 123 Such a Why does this work hmm wooowowow No }

It skips on the "Please Work" string, and I don't know why, or even how it works TBH, but yeah, it works, barely? And for the Int section, same, doesn't append the 5 to the LL.

{ 1 2 3 4 }

My Expectations were:

For String:

{ 123 Such a Why does this work }
{ 123 Such a Why does this work hmm wooowowow Please Work No }

For Int:

{ 1 2 3 4 5 }
1 2 3 4 5

Solution

  • After inserting std::cout to every function, it printed a lot of lines, I found your code crashed on:

    list.append_values("hmm");
    

    The variadic append function is inf recursive.

    Too many recursive function call -> stack overflow -> segmentation fault.

    Option1

    Rename at least one of the append function to distinguish them.

    #include <iostream>
    
    template<typename data_type>
    struct _node {
        data_type data{};
        _node<data_type>* link = nullptr;
        _node(data_type value) : data(value) {}
    };
    
    template<typename s_data_type>
    struct linked_list {
    private:
        typedef _node<s_data_type> node;
        typedef s_data_type dt;
        int _size{};
        node* head = nullptr;
        node* last = nullptr;
    
    public:
        template<typename ...Args> linked_list(Args ...arg) {
            (append_value(arg), ...); 
        }
    
        void append_value(const dt value) {
            if (this->head == nullptr) {
                head = new node(value);
                last = head;
            }
            else {
                last->link = new node(value);
                last = last->link;
            }
            _size++;
        }
    
        template<typename ...Args> void append_values(Args ...arg) {
            (append_value(arg), ...);
        }
    
        void println() {
            node* current = head;
            while (current != nullptr) {
                std::cout << current->data << " -> ";
                current = current->link;
            }
            std::cout << "nullptr" << std::endl;
        }
    };
    
    void println() {
        std::cout << std::endl;
    }
    
    int main() {
        linked_list<std::string> list("123", "Such a", "Why does this work");
        list.println();
        list.append_values("hmm");
        list.append_values("wooowowow", "Please Work");
        list.append_values("No");
        println();
        list.println();
    
        return 0;
    }
    

    Option2

    Use enable_if<bool, class> to conditionally enable templates.

    #include <type_traits>
    
    template<typename ...Args>
    typename std::enable_if<(sizeof...(Args) > 1), void>::type append(Args ...arg) {
        (append(arg), ...);
    }