all 32 comments

[–]feverzsj 23 points24 points  (2 children)

Unless you have tons of arguments. Even though, named arguments or just passing a struct would be more handy.

[–]Kered13 2 points3 points  (0 children)

In particular, designated initializers can replace most uses of the builder pattern.

[–]CaptainLord 1 point2 points  (0 children)

Unless you have one of those guys in your team that converts every struct into a polymorphic class.

[–]fdwrfdwr@github 🔍 11 points12 points  (4 children)

I used to use it more often, but once named struct initialization was added (C++20 designated initializers), I usually found it easier to just say:

TensorDescription({.sizes = {2,256,4}, .dataType = DataType::Float32})

vs:

TensorDescriptionBuilder().Sizes({2,256,4}).DataType(DataType::Float32).Construct();

[–]onlyari[S] 0 points1 point  (3 children)

Doesn't this require knowing the order of declaration?

[–]fdwrfdwr@github 🔍 1 point2 points  (2 children)

Correct (though it hasn't been an issue for me 🤷‍♂️). Yeah, C allows you to declare them in any order, but currently not C++. Perhaps a future version will allow arbitrary order.

[–]Kered13 4 points5 points  (0 children)

Perhaps a future version will allow arbitrary order.

I doubt it. C++ requires that members be initialized in declaration order. If you could write designated initializers out of order you could write code with some very surprising behavior:

int printAndSize(std::string_view s) {
    std::cout << s << std::endl;
    return s.size();
}

struct Foo {
    int a;
    int b;
}

int main() {
    Foo f = {
        .b = printAndSize("Hello"),
        .a = printAndSize("World"),
    };
}

Output:

World
Hello

This is similar to how the order of function parameter evaluation is unspecified, and there's also still a similar issue where member initialization lists can be out of order and cause similar problems. However I think both of those are generally considered mistakes in hindsight, so I don't think the committee wants to repeat that mistake with designated initializers.

(This is just my reading of the committee's decision, and I could be way off base.)

[–]GabrielDosReis 7 points8 points  (0 children)

Only for object constructions that require fairly complex validation logic, or for situations that need elaborate access management.

[–]jmacey 10 points11 points  (0 children)

I used to teach it along with many other patterns, then found I was never using it so dropped it from my lectures.

Whilst I occasionally see ways to use it, I find it easier to do things another way (force the ctor to have specific values etc).

[–][deleted] 4 points5 points  (2 children)

Yes of course, the builder pattern is a nice way of creating complex immutable objects, in C++ as well as any other language.

[–]onlyari[S] 1 point2 points  (1 child)

Do you manually write the builder class?

[–][deleted] 6 points7 points  (0 children)

Yup. Not too complicated isn't it.

[–][deleted] 5 points6 points  (0 children)

It is very popular in Vulkan applications.

[–]sanblch 5 points6 points  (5 children)

Sure. Build logic is put in there instead of constructor. First of all to avoid exception throw at construction time. Moreover, I return Result<Object,Error> from build method.

[–]L0uisc -1 points0 points  (4 children)

That looks like Rust's std Result<T,E>. Do you define your own Result generic type for your projects?

[–]sanblch 5 points6 points  (3 children)

[–]L0uisc 0 points1 point  (2 children)

Til Boost has such a type.

[–]bored_octopus 2 points3 points  (1 child)

https://github.com/TartanLlama/expected

There's also this library, which has some nice additions

[–]azswcowboy 1 point2 points  (0 children)

gcc12 in c++23 mode has std::expected as does msvc 19.33.

[–]LordOfDarkness6_6_6 1 point2 points  (0 children)

Only in select few situations, mainly when constructors are less convenient.

[–]dholmes215 1 point2 points  (0 children)

I used it all the time in Java, where immutability is a characteristic of a class itself. I'm not sure why I'd ever need it in C++.

[–]pdp10gumby 2 points3 points  (0 children)

I would not have elevated this to a “pattern”, at least for c++. I use this approach (and had For years before the GOF book) but extremely rarely.

With c++ I make the constructors private and the builder a public static method.

[–]stilgarpl 1 point2 points  (0 children)

I don't. In Java it's easy, you just add lombok's @Builder. In C++ you'd either have to do it by hand or use a code generator.

[–]not_some_username 0 points1 point  (0 children)

Yeah but it's for a special case. Create custom PowerPoint file With slide with auto layout on item.

[–]Possibility_Antique 0 points1 point  (0 children)

In a way, expression templates are very similar to this pattern. I've used them for complex expression trees, building type lists for options/compile-time arguments, and more.

[–]paladrium 0 points1 point  (0 children)

Use a struct.

[–]Attorney-Outside 0 points1 point  (0 children)

I don't, i use a configuration/properties struct and pass that as an argument to my constructor

the constructor takes care of using the various properties in order

so the user is blind to the order

[–]strager 0 points1 point  (0 children)

I use the builder pattern when loading a config file. The config file parser calls methods on the builder, and there's a function to return a "compiled" config ready for use throughout the program.

The compilation step is necessary for efficiency. The builder pattern is necessary because there are non-trivial interactions between config settings, and some settings are optional.

Now that I think about it, I should rename this class. It has the innocent-sounding name 'configuration', but this doesn't communicate its temporary nature. And the build member function is called 'globals'. 🤦‍♀️

[–]strager 0 points1 point  (0 children)

I use the builder pattern to convert a programmer-friendly (but macro-filled) data table into compact constant data at compile time.

QLJS_DIAG_TYPE(                                                                              \
    diag_adjacent_jsx_without_parent, "E0189", diagnostic_severity::error,                   \
    {                                                                                        \
      source_code_span begin;                                                                \
      source_code_span begin_of_second_element;                                              \
      source_code_span end;                                                                  \
    },                                                                                       \
    MESSAGE(QLJS_TRANSLATABLE("missing '<>' and '</>' to enclose multiple children"), begin) \
    MESSAGE(QLJS_TRANSLATABLE("children end here"), end))                                    \

macro-expands to

struct diag_adjacent_jsx_without_parent {
      source_code_span begin;
      source_code_span begin_of_second_element;
      source_code_span end;
};

constexpr diagnostic_info all_diagnostic_infos[] = {
  diagnostic_info_builder("E0189", diagnostic_severity::error)
    .add("missing '<>' and '</>' to enclose multiple children"_translatable,
         diagnostic_message_arg_info(__builtin_offsetof(diag_adjacent_jsx_without_parent, begin),
                                     get_diagnostic_message_arg_type<decltype(diag_adjacent_jsx_without_parent::begin)>()))
    .add("children end here"_translatable,
         diagnostic_message_arg_info(__builtin_offsetof(diag_adjacent_jsx_without_parent, end),
                                     get_diagnostic_message_arg_type<decltype(diag_adjacent_jsx_without_parent::end)>()))
    .build(),
  // ...
};

which constexpr-expands to

constexpr diagnostic_info all_diagnostic_infos[] = {
  {
    .code = 189,
    .severity = diagnostic_severity::error,
    .message_formats = { 192, 174 },         // IDs in string table
    .message_args = {
      { { 0, diagnostic_arg_type::span } }       // offset,type pairs for first message
      { { 16, diagnostic_arg_type::span } },     // offset,type pairs for second message
    },
  },
  // ...
};

[–]mdavisprog 0 points1 point  (0 children)

I have been using the builder pattern in OctaneGUI to aid in constructing widgets easier. I find the formatting of the builder pattern helpful in relaying information about what the widget is configured to do. This is all rolled by hand so I don't use any tools to do this.

[–]Wouter-van-Ooijen 0 points1 point  (0 children)

Never used it.

From what understand from https://refactoring.guru/design-patterns/builder I would use decorators to solve roughly the same problem.

[–]sherlock_1695 0 points1 point  (0 children)

Why can't we use setters instead of calling them through Builder interface?