---
source_url: https://www.pubnub.com/docs/sdks/windows-cpp/api-reference/publish-and-subscribe
title: Publish/Subscribe API for Windows C++ SDK
updated_at: 2026-05-19T12:14:06.804Z
sdk_name: PubNub Windows C++ SDK
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Publish/Subscribe API for Windows C++ SDK

PubNub Windows C++ SDK

## Publish

The `publish()` function is used to send a message to all subscribers of a channel. To publish a message you must first specify a valid `publish_key` at initialization. A successfully published message is replicated across the PubNub Real-Time Network and sent simultaneously to all subscribed clients on a channel.

Messages in transit can be secured from potential eavesdroppers with SSL/TLS by setting ssl to true during initialization.

#### Publish anytime

It's not required to be subscribed to a channel in order to publish to that channel.

#### Message data

The message argument can contain any JSON serializable data, including: Objects, Arrays, Ints and Strings. `data` should not contain special Windows C++ classes or functions as these will not serialize. String content can include any single-byte or multi-byte UTF-8 character.

:::warning JSON serialize
JSON serialize! It is important to note that you should JSON serialize when sending signals/messages via PUBNUB.
:::

#### Message size

The maximum number of characters per message is 32 KiB by default. The maximum message size is based on the final escaped character count, including the channel name. An ideal message size is under 1800 bytes which allows a message to be compressed and sent using single IP datagram (1.5 KiB) providing optimal network performance.

If the message you publish exceeds the configured size, you will receive the following message:

#### Message too large error

```cpp
["PUBLISHED",[0,"Message Too Large","13524237335750949"]]
```

For further details please check: [https://support.pubnub.com/hc/en-us/articles/360051495932-Calculating-Message-Payload-Size-Before-Publish](https://support.pubnub.com/hc/en-us/articles/360051495932-Calculating-Message-Payload-Size-Before-Publish)

#### Message publish rate

Messages can be published as fast as bandwidth conditions will allow. There is a soft limit based on max throughput since messages will be discarded if the subscriber can't keep pace with the publisher.

For example, if 200 messages are published simultaneously before a subscriber has had a chance to receive any messages, the subscriber may not receive the first 100 messages because the message queue has a limit of only 100 messages stored in memory.

#### Publishing to multiple channels

It is not possible to publish a message to multiple channels simultaneously. The message must be published to one channel at a time.

#### Publishing messages reliably

There are some best practices to ensure messages are delivered when publishing to a channel:

* Publish to any given channel in a serial manner (not concurrently).
* Check that the return code is success (e.g. `[1,"Sent","136074940..."]`)
* Publish the next message only after receiving a success return code.
* If a failure code is returned (`[0,"blah","<timetoken>"]`), retry the publish.
* Avoid exceeding the in-memory queue's capacity of 100 messages. An overflow situation (aka missed messages) can occur if slow subscribers fail to keep up with the publish pace in a given period of time.
* Throttle publish bursts in accordance with your app's latency needs e.g. Publish no faster than 5 msgs per second to any one channel.

### Method(s)

To `Publish a message` you can use the following method(s) in the Windows C++ SDK:

```cpp
publish (std::string const &channel, std::string const &message)
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| channel | std::string | Yes |  | Specifies `channel` ID to `publish` messages to. |
| message | std::string | Yes |  | The `message`. |

```cpp
publishv2 (std::string const &channel, std::string const &message, pubv2_opt options)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: std::string const & | Specifies `channel` ID to `publish` messages to. |
| `message` *Type: std::string const & | The `message` to publish. |
| `options` *Type: pubnub::pubv2_opt | Options for `Publish v2`. These are designed to be used as bit-masks, for which purpose there are overloaded `&` and ` |

### Sample code

#### Publish a message to a channel

```cpp
// Sync
void publish(pubnub::context &pn) {
  enum pubnub_res res;

  res = pn.publish("my_channel", "\"Hello from the PubNub C++ SDK!\"").await();

  if (PNR_OK == res) {
    std::cout << pn.last_publish_result() << std::endl;
  } else {
    std::cout << "Publish request failed" << std::endl;
  }
}

// Lambdas
void publish(pubnub::context &pn) {
  pn.publish("my_channel", "\"Hello from the PubNub C++ SDK!\"").
    then([=](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::cout << pn.last_publish_result() << std::endl;
      } else {
        std::cout << "Publish request failed" << std::endl;
      }
    });
}

// Functions
static void on_publish(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::cout << pn.last_publish_result() << std::endl;
  } else {
    std::cout << "Publish request failed" << std::endl;
  }
}

void publish(pubnub::context &pn) {
  pn.publish("my_channel", "\"Hello from the PubNub C++ SDK!\"").then(on_publish);
}
```

:::note Subscribe to the channel
Before running the above publish example, either using the [Debug Console](https://www.pubnub.com/docs/console/) or in a separate script running in a separate terminal window, [subscribe to the same channel](#subscribe) that is being published to.
:::

### Rest response from server

The function returns the following formatted response:

```json
[1, "Sent", "13769558699541401"]
```

### Other examples

#### Publish a JSON serialized message

```cpp
// Sync
void publish(pubnub::context &pn) {
  enum pubnub_res res;

  res = pn.publish("my_channel", "{\"msg\": \"Hello from the PubNub C++ SDK!\"}").await();

  if (PNR_OK == res) {
    std::cout << pn.last_publish_result() << std::endl;
  } else {
    std::cout << "Publish request failed" << std::endl;
  }
}

// Lambdas
void publish(pubnub::context &pn) {
  pn.publish("my_channel", "{\"msg\": \"Hello from the PubNub C++ SDK!\"}").
    then([=](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::cout << pn.last_publish_result() << std::endl;
      } else {
        std::cout << "Publish request failed" << std::endl;
      }
    });
}

// Functions
static void on_publish(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::cout << pn.last_publish_result() << std::endl;
  } else {
    std::cout << "Publish request failed" << std::endl;
  }
}

void publish(pubnub::context &pn) {
  pn.publish("my_channel", "{\"msg\": \"Hello from the PubNub C++ SDK!\"}").then(on_publish);
}
```

#### Publish with cipher key

```cpp
res = pubnub_publish_encrypted(pbp, chan, "\"Hello world from crypto sync!\"", cipher_key);
if (res != PNR_STARTED) {
    printf("pubnub_publish() returned unexpected: %d\n", res);
    pubnub_free(pbp);
    return -1;
}
```

#### Publish with cipher key using pubnub_publish_ex

Using this method you can reuse the `cipherKey` from the options.

```cpp
struct pubnub_publish_options opt = pubnub_publish_defopts();
opt.cipher_key = my_cipher_key;
pbresult = pubnub_publish_ex(pn, "my_channel", "42", opt);
```

## Signal

Sends a signal `@p` message on the `@p` channel via chosen `@p` method (GET by default).

By default, signals are limited to a message payload size of `64` bytes. This limit applies only to the payload, and not to the URI or headers. If you require a larger payload size, please [contact support](https://www.pubnub.com/docs/mailto:support@pubnub.com).

### Method(s)

#### Declaration

`futres signal(std::string const& channel, std::string const& message)`

#### Parameters

| Parameter | Description |
| --- | --- |
| `channel` *Type: std::string | Channel ID to send a signal to. |
| `message` *Type: std::string | Message to send (signal), in JSON format. |

### Sample code

#### Signal a message to a channel

```cpp
futres fr = pb.signal("my_channel", "\"singalling\"");
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `pubnub::futres` |  | Use to get the outcome of the transaction, in the same way as all other transactions |

## Subscribe

This function causes the client to create an open TCP socket to the PubNub Real-Time Network and begin listening for messages on a specified `channel` ID. To subscribe to a `channel` ID the client must send the appropriate `subscribe_key` at initialization. By default a newly subscribed client will only receive messages published to the channel after the `subscribe()` call completes.

:::note Context usage
Typically, you will want two separate contexts for publish and subscribe anyway. If you are changing the set of channels you subscribe to, you should first call `leave()` on the old set.
The `subscribe()` interface is essentially a transaction to start listening on the channel for arrival of next message. This has to be followed by a call to the method `get()` or `get_all( )` to retrieve the actual message, once the subscribe transaction completes successfully. This needs to be performed every time it is desired to retrieve a message from the channel.
:::

:::warning Unsubscribing from all channels
**Unsubscribing** from all channels, and then **subscribing** to a new channel Y is not the same as subscribing to channel Y and then unsubscribing from the previously-subscribed channel(s). Unsubscribing from all channels resets the last-received `timetoken` and thus, there could be some gaps in the subscription that may lead to message loss.
:::

### Method(s)

To `Subscribe to a channel` you can use the following method(s) in the Windows C++ SDK:

```cpp
subscribe (std::string const &channel, std::string const &channel_group="")
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: std::string const & | Specifies the `channel` ID to which to `subscribe` |
| `channel_group` *Type: std::string const & | Specifies `channel_group` to which to `subscribe` |

```cpp
subscribe (std::vector<std::string> const &channel, std::vector< std::string > const &channel_group)
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: `std::vector<std::string> const &` | Specifies `channel(s)` to which to `subscribe` |
| `channel_group` *Type: `std::vector<std::string> const &` | Specifies `channel_groups` to which to `subscribe` |

### Sample code

Subscribe to a channel:

```cpp
// Sync
void subscribe(pubnub::context &pn) {
  enum pubnub_res res;

  for (;;) {
    res = pn.subscribe("my_channel").await();

    if (PNR_OK == res) {
      std::vector<std::string> msg = pn.get_all();

      for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
       std::cout << *it << std::endl;
      }
    } else {
      std::cout << "Request failed" << std::endl;
      break;
    }
  }
}

// Lambdas
void subscribe(pubnub::context &ipn) {
  ipn.subscribe("my_channel").then([=](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
    auto msg = pn.get_all();

    if (PNR_OK == res) {
      for (auto &&m: msg) {
        std::cout << m << std::endl;
      }

    } else {
      std::cout << "Request failed" << std::endl;
    }
  });
}

// Functions
void on_subscribe(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::vector<std::string> msg = pn.get_all();

    for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
      std::cout << *it << std::endl;
    }
  } else {
    std::cout << "Request failed" << std::endl;
  }
}

void subscribe(pubnub::context &pn) {
  pn.subscribe("my_channel").then(on_subscribe);
}
```

### Rest response from server

The output below demonstrates the response format to a successful call:

```json
[[], "Time Token"]
```

### Other examples

#### Subscribing to multiple channels

It's possible to subscribe to more than one channel using the [Multiplexing](https://www.pubnub.com/docs/general/channels/subscribe#channel-multiplexing) feature. The example shows how to do that using an array to specify the channel names.

:::note Alternative subscription methods
You can also use [Wildcard Subscribe](https://www.pubnub.com/docs/general/channels/subscribe#wildcard-subscribe) and [Channel Groups](https://www.pubnub.com/docs/general/channels/subscribe#channel-groups) to subscribe to multiple channels at a time. To use these features, the *Stream Controller* add-on must be enabled on your keyset in the [Admin Portal](https://admin.pubnub.com).
:::

```cpp
//Sync
enum pubnub_res res;
res = pn.subscribe("my_channel1,my_channel2").await();

//Lambdas
enum pubnub_res res;
pn.subscribe("my_channel1,my_channel2").then(...);

//Functions
enum pubnub_res res;
pn.subscribe("my_channel1,my_channel2").then(on_connect);
```

#### Subscribing to a Presence channel

:::warning Requires Presence
This method requires that the Presence add-on is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). For information on how to receive presence events and what those events are, refer to [Presence Events](https://www.pubnub.com/docs/general/presence/presence-events#subscribe-to-presence-channel).
:::

For any given channel there is an associated Presence channel. You can subscribe directly to the channel by appending `-pnpres` to the channel name. For example the channel named `my_channel` would have the presence channel named `my_channel-pnpres`.

```cpp
// Sync
void presence(pubnub::context &pn) {
  enum pubnub_res res;
  bool done = false;

  while (!done) {
    res = pn.subscribe("my_channel-pnpres").await();

    if (PNR_OK == res) {
      std::vector<std::string> msg = pn.get_all();

      for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
        std::cout << *it << std::endl;
      }

      if (msg.size() > 0) {
        done = true;
      }
    } else {
      std::cout << "Error" << std::endl;
      break;
    }
  }
}

// Lambdas
void presence(pubnub::context &ipn) {
  bool done = false;

  while (!done) {
    ipn.subscribe("my_channel-pnpres").then([&](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
      auto msg = pn.get_all();

      if (PNR_OK == res) {
        for (auto &&m: msg) {
          std::cout << m << std::endl;
        }

        if (msg.size() > 0) {
          done = true;
        }
      } else {
        std::cout << "Request failed" << std::endl;
      }
    });
  }
}

// Functions
void on_presence(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::vector<std::string> msg = pn.get_all();

    for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
      std::cout << *it << std::endl;
    }

    if (msg.size() > 0) {
      done = true;
    }
  } else {
    std::cout << "Error" << std::endl;
  }
}

void presence(pubnub::context &pn) {
  while (!done) {
    pn.subscribe("my_channel-pnpres").then(on_presence);
  }
}
```

#### Sample Responses

#### Join event

```json
{
    "action": "join",
    "timestamp": 1345546797,
    "uuid": "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
    "occupancy": 2
}
```

#### Leave event

```json
{
    "action" : "leave",
    "timestamp" : 1345549797,
    "uuid" : "175c2c67-b2a9-470d-8f4b-1db94f90e39e",
    "occupancy" : 1
}
```

#### Timeout event

```json
{
    "action": "timeout",
    "timestamp": 1345549797,
    "uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    "occupancy": 0
}
```

#### State change event

```json
{
    "action": "state-change",
    "uuid": "76c2c571-9a2b-d074-b4f8-e93e09f49bd",
    "timestamp": 1345549797,
    "data": {
        "isTyping": true
    }
}
```

#### Interval event

```json
{
    "action":"interval",
    "timestamp":1474396578,
    "occupancy":2
}
```

When a channel is in interval mode with `presence_deltas` `pnconfig` flag enabled, the interval message may also include the following fields which contain an array of changed UUIDs since the last interval message.

* joined
* left
* timedout

For example, this interval message indicates there were 2 new UUIDs that joined and 1 timed out UUID since the last interval:

```json
{
    "action" : "interval",
    "occupancy" : <# users in channel>,
    "timestamp" : <unix timestamp>,
    "joined" : ["uuid2", "uuid3"],
    "timedout" : ["uuid1"]
}
```

If the full interval message is greater than `30KB` (since the max publish payload is `∼32KB`), none of the extra fields will be present. Instead there will be a `here_now_refresh` boolean field set to `true`. This indicates to the user that they should do a `hereNow` request to get the complete list of users present in the channel.

```json
{
    "action" : "interval",
    "occupancy" : <# users in channel>,
    "timestamp" : <unix timestamp>,
    "here_now_refresh" : true
}
```

#### Subscribe to a channel group

:::note Requires Stream Controller add-on
This method requires that the *Stream Controller* add-on is enabled for your key in the [Admin Portal](https://admin.pubnub.com/). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

```cpp
//Sync
static void subscribe_to_group(pubnub::context &pn) {
  enum pubnub_res res;

  for (;;) {
    try {
      res = pn.subscribe("", channel_group).await();

      if (PNR_OK == res) {
        std::vector<std::string> msg = pn.get_all();

        for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
          std::cout << *it << std::endl;
        }

      } else {
        std::cout << "Failed with code " << res << std::endl;
      }
    } catch (std::exception &ex) {
      std::cout << "Exception: " << ex.what() << std::endl;
      break;
    }
  }
}

//Lambdas
static void subscribe_to_group(pubnub::context &ipn) {
  ipn.subscribe("", channel_group)
    .then([=](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::cout << "Susbcribed!" << std::endl;

        pn.subscribe("", channel_group)
          .then([=](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
            if (PNR_OK == res) {
              auto msg = pn.get_all();
              for (auto &&x: msg) {
                std::cout << x << std::endl;
              }
            } else {
              std::cout << "Failed with code " << res << std::endl;
            }
        });
      } else {
        std::cout << "Failed with code " << res << std::endl;
      }
  });
}

//Functions
static void on_subscribe(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::vector<std::string> msg = pn.get_all();

    for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
      std::cout << *it << std::endl;
    }

  } else {
    std::cout << "Failed with code " << res << std::endl;
  }
}

static void on_first_subscribe(pubnub::context &pb, pubnub_res res)
{
  if (PNR_OK ==  res) {
    std::cout << "Subscribed!" << std::endl;
  } else {
    std::cout << "Subscribe failed!" << std::endl;
  }

  pb.subscribe("", channel_group).then(on_subscribe);
}

static void subscribe_to_group(pubnub::context &ipn) {
  ipn.subscribe("", channel_group).then(on_first_subscribe);
}
```

#### Subscribe to the Presence channel of a channel group

:::note Requires Stream Controller and Presence add-ons
This method requires both the *Stream Controller* and *Presence* add-ons are enabled for your key in the [Admin Portal](https://admin.pubnub.com/). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

```cpp
//Sync
static void presence_of_group(pubnub::context &pn) {
  enum pubnub_res res;

  for (;;) {
    try {
      res = pn.subscribe("", channel_group + "-pnpres").await();

      if (PNR_OK == res) {
        std::vector<std::string> msg = pn.get_all();

        for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
          std::cout << *it << std::endl;
        }

      } else {
        std::cout << "Failed with code " << res << std::endl;
      }
    } catch (std::exception &ex) {
      std::cout << "Exception: " << ex.what() << std::endl;
      break;
    }
  }
}

//Lambdas
static void presence_of_group(pubnub::context &ipn) {
  ipn.subscribe("", channel_group + "-pnpres")
    .then([=](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
      if (PNR_OK == res) {
        std::cout << "Susbcribed!" << std::endl;

        pn.subscribe("", channel_group + "-pnpres")
          .then([=](https://www.pubnub.com/docs/pubnub::context &pn, pubnub_res res) {
            if (PNR_OK == res) {
              auto msg = pn.get_all();
              for (auto &&x: msg) {
                std::cout << x << std::endl;
              }
            } else {
              std::cout << "Failed with code " << res << std::endl;
            }
        });
      } else {
        std::cout << "Failed with code " << res << std::endl;
      }
  });
}

//Functions
static void on_subscribe(pubnub::context &pn, pubnub_res res) {
  if (PNR_OK == res) {
    std::vector<std::string> msg = pn.get_all();

    for (std::vector<std::string>::iterator it = msg.begin(); it != msg.end(); ++it) {
      std::cout << *it << std::endl;
    }

  } else {
    std::cout << "Failed with code " << res << std::endl;
  }
}

static void on_first_subscribe(pubnub::context &pb, pubnub_res res)
{
  if (PNR_OK ==  res) {
    std::cout << "Subscribed!" << std::endl;
  } else {
    std::cout << "Subscribe failed!" << std::endl;
  }

  pb.subscribe("", channel_group + "-pnpres").then(on_subscribe);
}

static void subscribe_to_group(pubnub::context &ipn) {
  ipn.subscribe("", channel_group + "-pnpres").then(on_first_subscribe);
}
```

#### Using cipherKey with subscribe

For subscribe, you don't use the cipher key at subscribe, but, when you get the received messages after the subscribe transaction has finished:

```cpp
char msg[MAX_MSG_LEN];
pbresult = pubnub_get_decrypted(pn, my_cipher_key, s, sizeof s);
if (PNR_OK == pbresult) {
    /* Use the message in `msg` */
}
```

or, for a different usability/safety trade-off:

```cpp
pubnub_bymebl_t msg = pubnub_get_decrypted_alloc(pn, my_cipher_key);
if (msg.ptr != NULL) {
    /* use the message in `msg.ptr` */
    free(msg.ptr);
}
```

## Subscribe v2

### Subscribe v2 options structure

Starts a subscribe V2 transaction on the channel (or channels) `@p` channel, with options `@p` opt.

### Method(s)

#### Declaration

`futres subscribe_v2(std::string const& channel, subscribe_v2_options opt); futres subscribe_v2(std::vector<std::string> const& channels, subscribe_v2_options opt);`

#### Members

| Parameter | Description |
| --- | --- |
| `channel` *Type: `std::string` | Channel ID to start a subscribe V2 on. |
| `channels` *Type: `std::vector<std::string>` | Vector of channels to start a subscribe V2 on. |
| `opt` *Type: `subscribe_v2_options` | Options of the subscribe V2 to start (channel group, heartbeat, filter...). |

### Sample code

```cpp
futres fr = pb.subscribe_v2("my_channel", subscribe_v2_options());
```

### Subscribe to a channel group example

```cpp
futres fr = pb.subscribe_v2("", subscribe_v2_options().channel_group("my_group"));
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `pubnub::futres` |  | Used to get the outcome of the transaction, like for any other transaction. |

### Get V2 message

Returns the next v2 message from the context. If there are none left, returns an empty message structure (checked through `v2_mesage::is_empty()`)

### Method(s)

#### Declaration

`v2_message get_v2() const;`

#### Parameter

none

### Sample code

```cpp
auto v2_msg = pb.get_v2();
    if (v2_msg.is_empty()) {
        std::cout << "No more messages in the context\n";
    }
    else {
        std::cout << "Received message on channel:" << v2_msg.channel()
            << "\n  match (or group): " << v2_msg.match_or_group()
            << "\n  payload: " << v2_msg.payload() << "\n";
}
```

### Get all V2 messages

Returns a vector of all v2 messages from the context. If none are left, returned vector will be empty.

### Method(s)

#### Declaration

`std::vector<v2_message> get_all_v2() const;`

#### Parameter

none

### Sample code

```cpp
for (auto v2_msg: pb.get_all_v2()) {
        std::cout << "Received message on channel:" << v2_msg.channel()
            << "\n  match (or group): " << v2_msg.match_or_group()
            << "\n  payload: " << v2_msg.payload() << "\n";
}
```

### Subscribe V2 options class

A wrapper class for subscribe_v2 options structure, enabling a nicer usage. Something like:

### Method(s)

#### Declaration

`pn.subscribe(chan, subscribe_v2_options().heartbeat(412));`

#### Parameter

| Member | Parameter | Return | Description |
| --- | --- | --- | --- |
| `Default constructor` |  |  | Initializes options to their defaults |
| `channel_or_group` | `std::string const& chgroup` | *this | Sets channel group option. It's copied into this object, so the user need not keep `chgroup` alive any further more. |
|  | `std::vector<std::string> const& chgroup` | *this | Helper method to set channel group via a vector of channel groups |
| `heartbeat` | `unsigned hb_interval` | *this | Sets the heartbeat interval |
| `filter_expr` | `std::string const* filter_expr` | *this | Sets the filter expression to use (expression is Javascript-like). It's copied into this object, so the user need not keep `filter_expr` alive any further more |
| `data` | (none) | `pubnub_subscribe_v2_options` | Returns the value of the `wrapped` subscribe v2 options structure |

### Message V2 class

A wrapper class for pubnub_v2_message structure, enabling a nicer usage, creating std::strings from character buffers and providing some helper functions, such as `is_empty()`.

| Member | Parameter | Return | Description |
| --- | --- | --- | --- |
| `Default constructor` |  |  | Initializes all message elements to zeroes (empty). |
| `constructor` | pubnub_v2_message message_v2 | *this | Initializes the wrapped message to the given `message_v2` |
| `tt` | (none) | std::string | The timetoken of the message |
| `region` | (none) | int | The region of the message |
| `flags` | (none) | int | The flags of the message |
| `channel` | (none) | std::string | The matched channel ID or group |
| `payload` | (none) | std::string | The payload of the message |
| `metadata` | (none) | pubnub_message_type | The type of the message (published, signal) |
| `is_empty` | (none) | bool | Returns whether the message is empty (there is no message actually, this object is a placeholder). Message is considered empty if there is no timetoken info(whose existence is obligatory for any valid v2 message). This does *not* mean that the payload of the message is empty, as it cannot be, it has to be in JSON format. |

## Unsubscribe

To `unsubscribe`, you need to cancel a subscribe transaction.

* If you configured SDK to be thread-safe, you can cancel at any time, but, the cancelling may actually fail - i.e., your thread may wait for another thread to finish working with the context, and by the time your cancel request gets processed, the transaction may finish.
* If you configured SDK to not be thread-safe, the only safe way to do it is to use the `sync` interface and:

1. Set the context to use `non-blocking I/O`.
2. Wait for the outcome in a loop, checking for `pubnub_last_result()` - rather than calling `pubnub_await()`.
3. If a condition occurs that prompts you to `unsubscribe`, call [cancel()](https://www.pubnub.com/docs/sdks/windows-cpp/api-reference/misc#pubnub-cancel).
4. Wait for the cancellation to finish (here you can call `pubnub_await()`, unless you want to do other stuff while you wait).

:::warning Unsubscribing from all channels
**Unsubscribing** from all channels, and then **subscribing** to a new channel Y is not the same as subscribing to channel Y and then unsubscribing from the previously-subscribed channel(s). Unsubscribing from all channels resets the last-received `timetoken` and thus, there could be some gaps in the subscription that may lead to message loss.
:::

### Method(s)

To `Unsubscribe from a channel` you can use the following method(s) in the Windows C++ SDK:

1. [Go to PubNub Leave Method](https://www.pubnub.com/docs/sdks/windows-cpp/api-reference/misc#pubnub-leave).

### Sample code

Unsubscribe from a channel:

```cpp
auto futr = ctx.subscribe( "my_channel");
/* If we don't set non-blocking I/O, we can't get out of a blocked read */
ctx.set_blocking_io(pubnub::blocking);
/* Can't use await() here, it will block */
auto pbresult = PNR_STARTED;
while (PNR_STARTED == pbresult) {
     pbresult = futr.last_result();
     /* Somehow decide we want to quit / unsubscribe */
     if (should_stop()) {
         ctx.cancel();
         /* If we don't have anything else to do, it's OK to await now,
          * but you could again have a loop "against" pubnub_last_result()
          */
         pbresult = futr.await();
         break;
     }
}
if (PNR_CANCELLED == pbresult) {
    std::cout << "Subscribe cancelled - unsubscribed!" << std::endl;
}
```

### Rest response from server

The output below demonstrates the response to a successful call:

```json
{
    "action" : "leave"
}
```