# Invoking Methods

For both `Places` and `Agents`, we can invoke a member method on all valid instances using the `callAll` function. The requested method will be executed on all valid `Place` or `Agent` instances concurrently. 

The `callAll` function, provided by both `Places` and `Agents`, requires two arguments:

* `functionId`: The integer id identifying the member method to be invoked. 
* `argument`: The argument value for invoking the member method, which will be dispatched to all the instances. This argument is optional, if the requested method takes no arguments, it can be omitted.

Note that the second argument of the `callAll` method can only be one of the two following cases: 

* A single non-pointer trivially copyable value, such as an integer, float, or classes containing only trivially copyable member variables. Typical counterexample includes `std::vector`, `std::string` etc.
* A container with contiguous storage with trivially copyable elements. To be precise, all containers that can be accepted by `std::data()` and `std::size()` methods are valid. Typical counterexample includes `std::map` (it is a container, but not contiguous) and `std::vector<std::vector<int>>` (elements are not trivially copyable). 

```{important}
The `std::pair` and `std::tuple` types seems to be valid, but they are actually not trivially copyable in C++ (blame the C++ std committee for that). In this case, either use `cuda::std::pair` / `cuda::std::tuple` or define your own struct to hold the values. 
```

The behavior of `callAll` for `Places` and `Agents` is very similar, but differs slightly due to different definitions for "valid" instances. Usually users are not required to pay attention to such details, but it is still good to know. 

* For `Places`, `callAll` will only be invoked on instances that are not within the Ghost Regions.
* For `Agents`, `callAll` will only be invoked on instances that are alive. In other word, if an instance is terminated, it will not be invoked by `callAll`.

Consider an example of a custom 2D `Place` class named `MyPlace`, which contains a member method named `myFunc` that takes in an integer argument. To correctly invoke it through `callAll`, we need the following steps: 

1. Assign an integer id for this method. This is typically done by defining an enum.
2. Route the method invocation requests from `callMethod` to `myFunc`
3. In the simulation loop, invoke `callAll` with the id and necessary arguments. 

```cpp
class MyPlace final : public mass::Place {
public:
    __universal__ explicit MyPlace(const mass::Index localIndex): Place(localIndex) {}

    enum Function {
        MY_FUNC     // The id assigned to `myFunc`
    };

    __device__ void callMethod(const FunctionId functionId, void*const arg) {
        switch (functionId) {
            case MY_FUNC:
                myFunc(*reinterpret_cast<int*>(arg));
                break;
            default: 
                break;
        }
    }

    __device__ void myFunc(const int arg) {
        // implementation ...
    }
}
```

```cpp
// In simulation loop
places->callAll(
    MyPlace::Function::MY_FUNC,     // Id of the member method to call
    100                             // Argument to be passed to the member method
);
```


## Invoking Methods on Places with Different Arguments for each Instance

Sometimes a simulation may need to invoke a member method on each `Place` instance while providing different argument value for each of them. This is particularly useful for initializing the instances at the beginning. For this use case, we can use a separated function named `callAllWithPerPlaceArg`. 

Similar to `callAll`, it also takes in the identifier of the member method and the argument to be passed to the member method. However, the second argument has to be a container with contiguous storage containing the different argument value for each `Place` instance. The size of this container must be equal to the total number of `Place` instances in the `Places` grid. Then before calling the requested member method, use the local index provided by the `getLocalIndex` method to retrieve the argument value correspond to the current instance. 

```cpp
class MyPlace final : public mass::Place {
public:
    __universal__ explicit MyPlace(const mass::Index localIndex): Place(localIndex) {}

    enum Function {
        MY_FUNC     // The id assigned to `myFunc`
    };

    __device__ void callMethod(const FunctionId functionId, void*const arg) {
        switch (functionId) {
            case MY_FUNC:
                // retrieve the argument value for this instance using the local index.
                myFunc(reinterpret_cast<int*>(arg)[getLocalIndex()]);
                break;
            default: 
                break;
        }
    }

    __device__ void myFunc(const int arg) {
        // implementation ...
    }
}
```

```cpp
// In simulation loop
const auto args = std::vector<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
places->callAllWithPerPlaceArg(
    MyPlace::Function::MY_FUNC,     // Id of the member method to call
    args                            // Per-instance argument values
);
```


## Initialize Agents by Invoking Methods with Different Arguments for each Instance 

Similar to `Places`, sometime we may also need to invoke a member method on each `Agent` instance while providing different argument value for each of them. However, due to current limitation, we are not able to do this any time, especially after some instances have been terminated or migrated. As a result, the API designed for this use case, named `initializeInitialAgents`, can only be invoked at the beginning of a simulation before any `Agent` instances terminate, migrate or spawn any children. 

Similar to `callAllWithPerPlaceArg`, `initializeInitialAgents` takes in the identifier of the member method and a container with contiguous storage containing the different argument value for each `Agent` instance. The size of this container must be equal to the total number of initial `Agent` instances globally across all devices. And again, before invoking the requested member method, use the local index provided by the `getLocalIndex` method to retrieve the argument value correspond to the current instance. 

```cpp
class MyAgent final : public mass::Agent {
public: 
    __universal__ explicit MyAgent(const mass::Index localIndex) : Agent(localIndex) {}

    enum Function {
        INIT   // The id assigned to `init`
    };

    __device__ void callMethod(const FunctionId functionId, void*const arg) {
        switch (functionId) {
            case INIT:
                // retrieve the argument value for this instance using the local index.
                init(reinterpret_cast<int*>(arg)[getLocalIndex()]);
                break;
            default: 
                break;
        }
    }

    __device__ void init(const int arg) {
        // implementation ...
    }
};
```

```cpp
// In simulation loop
const auto args = std::vector<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
agents->initializeInitialAgents(
    MyAgent::Function::INIT,    // Id of the member method to call
    args                        // Per-instance argument values
);
```