Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save cvlabsio/33c4d819a14f0f977a9960786aaa261f to your computer and use it in GitHub Desktop.

Select an option

Save cvlabsio/33c4d819a14f0f977a9960786aaa261f to your computer and use it in GitHub Desktop.

Revisions

  1. @EvanMcBroom EvanMcBroom revised this gist Oct 6, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion pic-and-string-literals-2.md
    Original file line number Diff line number Diff line change
    @@ -57,7 +57,7 @@ constexpr char picString2[]{ "abc" };

    _"Woohoo!"_
    There is no more need to break out a string literal to use it in PIC code.
    The technique can also be used in a macro function to ensure that the `constexpr` keyword is used.
    The technique can also be used in a macro function to ensure that the `constexpr` keyword is not forgotten.


    ```c++
  2. @EvanMcBroom EvanMcBroom revised this gist Oct 6, 2021. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions pic-and-string-literals-2.md
    Original file line number Diff line number Diff line change
    @@ -27,14 +27,14 @@ The below code is how to create an `initializer_list` to do just that but there
    // Return an initializer sequence (ex. {'a', 'b', 'c'})
    template <typename T, std::size_t N, std::size_t... Index>
    constexpr std::initializer_list<char> make_init_seq(const T(&input)[N], const std::index_sequence<Index...>) {
    return { input[Index]... };
    return { input[Index]... };
    }

    // Return an initializer sequence for a given string literal
    template <typename T, std::size_t N>
    constexpr auto str_literal_init_seq(const T(&input)[N]) {
    constexpr std::make_index_sequence<N> sequence;
    return make_init_seq(std::forward<decltype(input)>(input), sequence);
    constexpr std::make_index_sequence<N> sequence;
    return make_init_seq(std::forward<decltype(input)>(input), sequence);
    }
    ```
    @@ -65,7 +65,7 @@ The technique can also be used in a macro function to ensure that the `constexpr
    #define pic_wstring(NAME, STRING) constexpr wchar_t NAME[]{ STRING }

    void f() {
    pic_string(picString, "Hello World!\n");
    pic_string(picString, "Hello World!\n");
    std::cout << picString;
    }
    ```
  3. @EvanMcBroom EvanMcBroom created this gist Oct 6, 2021.
    71 changes: 71 additions & 0 deletions pic-and-string-literals-2.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,71 @@
    # PIC and String Literals Part 2

    I previously wrote about how to use macro metaprogramming to simplify using string literals in position independent code (PIC).
    The results are summarized in the below code snippet and the article [can be read on GitHub](https://gist.github.com/EvanMcBroom/f5b1bc53977865773802d795ade67273).

    ```cpp
    void f() {
    // Example 1: The Pic idiom for instantiating a string
    char picString1[]{ 'a', 'b', 'c' };

    // Example 2: Using a metaprogramming technique from the
    // previous article to achieve the same result
    PIC_STRING(picString2, 5, "abc\n");
    }
    ```

    I resently revisited the topic because I was bothered by the solution's reliance on macro metaprogramming and a third party library to accomplish such a small goal.

    ## Template Metaprogramming

    A possible alternative solution was to use template metaprogramming which would likely not require much code or a third party library.
    Ideally we would use a template function to generate the initializer list of characters to instantiate a local character array at compile time.

    The below code is how to create an `initializer_list` to do just that but there is one issue....

    ```c++
    // Return an initializer sequence (ex. {'a', 'b', 'c'})
    template <typename T, std::size_t N, std::size_t... Index>
    constexpr std::initializer_list<char> make_init_seq(const T(&input)[N], const std::index_sequence<Index...>) {
    return { input[Index]... };
    }

    // Return an initializer sequence for a given string literal
    template <typename T, std::size_t N>
    constexpr auto str_literal_init_seq(const T(&input)[N]) {
    constexpr std::make_index_sequence<N> sequence;
    return make_init_seq(std::forward<decltype(input)>(input), sequence);
    }
    ```
    The `initializer_list` class that is created in `make_init_seq` will internally store pointers to the start and end of the `input` array.
    Ultimately that means that the return statement can not be used in a constant expression, and in turn, neither function as well.
    Meaning that any string literal used in conjunction with calling `str_literal_init_seq` will require the string to be stored in the `.data` section.
    Although approaching the problem by using templates to generate an initializer list of character arrays seemed promising at first, creating a solution using this technique was not viable.
    ## The Perfect Solution
    Luckily, the previous investigation caused me to stumble across the assembly that is generated for local arrays that are marked as `constexpr`.
    What is generated is _exactly_ the PIC assembly that I have been trying to achieve regardless of what compiler optimizations are enabled!
    Knowing this, the below local character array initializations generate the same assembly code.
    ```c++
    char picString1[]{ 'a', 'b', 'c' };
    constexpr char picString2[]{ "abc" };
    ```

    _"Woohoo!"_
    There is no more need to break out a string literal to use it in PIC code.
    The technique can also be used in a macro function to ensure that the `constexpr` keyword is used.


    ```c++
    #define pic_string(NAME, STRING) constexpr char NAME[]{ STRING }
    #define pic_wstring(NAME, STRING) constexpr wchar_t NAME[]{ STRING }

    void f() {
    pic_string(picString, "Hello World!\n");
    std::cout << picString;
    }
    ```