Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nlohmann::json::array 'push_back' is ambiguous #3589

Closed
2 tasks
ceandrade opened this issue Jul 18, 2022 · 2 comments
Closed
2 tasks

nlohmann::json::array 'push_back' is ambiguous #3589

ceandrade opened this issue Jul 18, 2022 · 2 comments
Labels
kind: bug solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@ceandrade
Copy link

ceandrade commented Jul 18, 2022

Description

This is an issue related to issue #235

I'm trying to keep the insertion order on nested objects. Using the following solution, we lose the nested insertion order:

#include "third_part/json/json.hpp"
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    nlohmann::ordered_json json;

    json["first"] = "first arg";
    json["second"] = "second arg";

    // Now, I have a list of tuples, shcih keys' order I need to preserve.
    auto my_tuples = nlohmann::json::array();

    my_tuples.push_back({
        {"node", "a"},
        {"old_value", 0},
        {"new_value", 1}
    });
    my_tuples.push_back({
        {"node", "b"},
        {"old_value", 10},
        {"new_value", 20}
    });

    json["my_tuples"] = my_tuples;

    cout << "First try:\n" << setw(2) << json << "\n";

    return 0;
}

This is the output:

{
  "first": "first arg",
  "second": "second arg",
  "my_tuples": [
    {
      "new_value": 1,
      "node": "a",
      "old_value": 0
    },
    {
      "new_value": 20,
      "node": "b",
      "old_value": 10
    }
  ]
}

Note that in the nested list, we have the keys in alphabetical order rather than insertion order.

Now, I tried to use a nested nlohmann::ordered_json in these ways:

#include "third_part/json/json.hpp"
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    nlohmann::ordered_json json;

    json["first"] = "first arg";
    json["second"] = "second arg";

    // Now, I have a list of tuples, shcih keys' order I need to preserve.
    auto my_tuples = nlohmann::json::array();

    my_tuples.push_back(nlohmann::ordered_json({
        {"node", "a"},
        {"old_value", 0},
        {"new_value", 1}
    }));

    auto tmp = nlohmann::ordered_json({
        {"node", "b"},
        {"old_value", 10},
        {"new_value", 20}
    });
    my_tuples.push_back(tmp);

    json["my_tuples"] = my_tuples;

    cout << "First try:\n" << setw(2) << json << "\n";

    return 0;
}

However, the compiler complains in both cases:

$ g++ -std=c++17 -O3 -I. test/test_json.cpp -o test_json
test/test_json.cpp: In function 'int main()':
test/test_json.cpp:20:7: error: call of overloaded 'push_back(nlohmann::ordered_json)' is ambiguous
   20 |     }));
      |       ^
In file included from test/test_json.cpp:1:
./third_part/json/json.hpp:19986:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
19986 |     void push_back(basic_json&& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20019:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
20019 |     void push_back(const basic_json& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20051:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type = std::pair<const std::__cxx11::basic_string<char>, nlohmann::basic_json<> >]'
20051 |     void push_back(const typename object_t::value_type& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20082:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t = std::initializer_list<nlohmann::detail::json_ref<nlohmann::basic_json<> > >]'
20082 |     void push_back(initializer_list_t init)
      |          ^~~~~~~~~
test/test_json.cpp:27:28: error: call of overloaded 'push_back(nlohmann::basic_json<nlohmann::ordered_map>&)' is ambiguous
   27 |     my_tuples.push_back(tmp);
      |                            ^
In file included from test/test_json.cpp:1:
./third_part/json/json.hpp:19986:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
19986 |     void push_back(basic_json&& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20019:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
20019 |     void push_back(const basic_json& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20051:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type = std::pair<const std::__cxx11::basic_string<char>, nlohmann::basic_json<> >]'
20051 |     void push_back(const typename object_t::value_type& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20082:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t = std::initializer_list<nlohmann::detail::json_ref<nlohmann::basic_json<> > >]'
20082 |     void push_back(initializer_list_t init)
      |          ^~~~~~~~~

So, this is close to issue #235, although we have a series of template resolution that looks like a bit different here.

Is this real bug? Can I keep the inserting order on nested objects in some way.

Thanks,

Carlos

Reproduction steps

Please, compile the given code using GNU G++ (MacPorts gcc10 10.3.0_1) or Clang++ (Apple clang version 12.0.5 (clang-1205.0.22.11))

Expected vs. actual results

Expected the nested keys in the inserted order. Actual results are not produced due to compiling error.

Minimal code example

#include "third_part/json/json.hpp"
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    nlohmann::ordered_json json;

    json["first"] = "first arg";
    json["second"] = "second arg";

    // Now, I have a list of tuples, shcih keys' order I need to preserve.
    auto my_tuples = nlohmann::json::array();

    my_tuples.push_back(nlohmann::ordered_json({
        {"node", "a"},
        {"old_value", 0},
        {"new_value", 1}
    }));

    auto tmp = nlohmann::ordered_json({
        {"node", "b"},
        {"old_value", 10},
        {"new_value", 20}
    });
    my_tuples.push_back(tmp);

    json["my_tuples"] = my_tuples;

    cout << "First try:\n" << setw(2) << json << "\n";

    return 0;
}

Error messages

$ g++ -std=c++17 -O3 -I. test/test_json.cpp -o test_json
test/test_json.cpp: In function 'int main()':
test/test_json.cpp:20:7: error: call of overloaded 'push_back(nlohmann::ordered_json)' is ambiguous
   20 |     }));
      |       ^
In file included from test/test_json.cpp:1:
./third_part/json/json.hpp:19986:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
19986 |     void push_back(basic_json&& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20019:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
20019 |     void push_back(const basic_json& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20051:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type = std::pair<const std::__cxx11::basic_string<char>, nlohmann::basic_json<> >]'
20051 |     void push_back(const typename object_t::value_type& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20082:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t = std::initializer_list<nlohmann::detail::json_ref<nlohmann::basic_json<> > >]'
20082 |     void push_back(initializer_list_t init)
      |          ^~~~~~~~~
test/test_json.cpp:27:28: error: call of overloaded 'push_back(nlohmann::basic_json<nlohmann::ordered_map>&)' is ambiguous
   27 |     my_tuples.push_back(tmp);
      |                            ^
In file included from test/test_json.cpp:1:
./third_part/json/json.hpp:19986:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
19986 |     void push_back(basic_json&& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20019:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>]'
20019 |     void push_back(const basic_json& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20051:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(const typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; typename nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::object_t::value_type = std::pair<const std::__cxx11::basic_string<char>, nlohmann::basic_json<> >]'
20051 |     void push_back(const typename object_t::value_type& val)
      |          ^~~~~~~~~
./third_part/json/json.hpp:20082:10: note: candidate: 'void nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::push_back(nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; BinaryType = std::vector<unsigned char>; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType>::initializer_list_t = std::initializer_list<nlohmann::detail::json_ref<nlohmann::basic_json<> > >]'
20082 |     void push_back(initializer_list_t init)
      |          ^~~~~~~~~

$ clang++ -std=c++17 -O3 -I. test/test_json.cpp -o test_json
test/test_json.cpp:16:15: error: call to member function 'push_back' is ambiguous
    my_tuples.push_back(nlohmann::ordered_json({
    ~~~~~~~~~~^~~~~~~~~
./third_part/json/json.hpp:19986:10: note: candidate function
    void push_back(basic_json&& val)
         ^
./third_part/json/json.hpp:20019:10: note: candidate function
    void push_back(const basic_json& val)
         ^
./third_part/json/json.hpp:20051:10: note: candidate function
    void push_back(const typename object_t::value_type& val)
         ^
./third_part/json/json.hpp:20082:10: note: candidate function
    void push_back(initializer_list_t init)
         ^
test/test_json.cpp:27:15: error: call to member function 'push_back' is ambiguous
    my_tuples.push_back(tmp);
    ~~~~~~~~~~^~~~~~~~~
./third_part/json/json.hpp:19986:10: note: candidate function
    void push_back(basic_json&& val)
         ^
./third_part/json/json.hpp:20019:10: note: candidate function
    void push_back(const basic_json& val)
         ^
./third_part/json/json.hpp:20051:10: note: candidate function
    void push_back(const typename object_t::value_type& val)
         ^
./third_part/json/json.hpp:20082:10: note: candidate function
    void push_back(initializer_list_t init)
         ^
2 errors generated.

Compiler and operating system

Mac OS X Big Sur, GNU G++ (MacPorts gcc10 10.3.0_1) or Clang++ (Apple clang version 12.0.5 (clang-1205.0.22.11))

Library version

Stable 3.10.5

Validation

@falbrechtskirchinger
Copy link
Contributor

falbrechtskirchinger commented Jul 18, 2022

You're mixing nlohmann::json and nlohmann::ordered_json. Don't.

Specifically, here:

auto my_tuples = nlohmann::json::array();

Use:

auto my_tuples = nlohmann::ordered_json::array();

@ceandrade
Copy link
Author

Oh, I see. Indeed, very simple! Thank for the extremely fast reply!

@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Jul 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: bug solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

No branches or pull requests

3 participants