"the IP for other users to connect to?"
Onion services don't use IPs. You should use SOCKS5's remote hostname functionality, this hands over the hostname to Tor and will ensure you do not try to resolve the onion address locally and potentially leak DNS. I have a simple C implementation of SOCKS5 intended for use with Tor here.
"configure Tor to create a hidden service"
You probably want to use the control port for this. You can create and maintain a connection to the control port and use the
ADD_ONION request to create the onion. This will make the app more portable, one of the issues with ricochet was that it (until recently) came with its own copy of Tor and used that exclusively which mean in tor enforcing environments like Whonix or Subgraph it resulted in Tor-over-Tor situations.
"Could I connect to [any] port [...] to avoid http requests?"
Yes, onion services are not exclusively http, it's just a very common use case for them. They can transport any kind of arbitrary TCP connection, as exampled by ricochet (mentioned above) which uses it's own protocol.
Some thoughts I'd like to add
Ricochet is already an extensible peer-to-peer service written in C++. You should consider making your functionality a general extension of ricochet. This will remove a lot of the effort on your part of re-inventing the wheel.
Imagine a scenario where two users wish to establish a peer-to-peer channel over onions. Alice wants to talk to Bob, so Alice's client makes a connection to bob.onion. Alice is able to establish a connection and talk to Bob and Alice knows that she is talking to Bob with reasonable certainty because Bob has shown proof of ownership of the public key that the b.onion address is derived from...but how does Bob know that the incoming connection is from Alice? All Bob can see is a connection from his Tor instance to his service.
Two possible solutions that are currently implemented are:
mod_onions for Prosody in XMPP. This uses a pre-existing XMPP extension XEP-0220. In this scenario, Bob provides the incoming claimed Alice with a cryptographic nonce and then Bob makes a connection to Alice's onion. Bob then requests the nonce value through the outbound connection made, this proves that the incoming Alice connection and the connection he made out to Alice are, at least, controlled by the same entity. On DNS this protection is quite weak, on onions this is a much stronger form of verification.
ricochet instead requires that the incoming Alice connection take a 16 byte value provided by Bob concatenated with another 16 byte value provided by Alice as the key to an HMAC-SHA256 of Alice's onion address and Bob's onion address concatenated. This resulting hash is then signed by Alice's onion's private key. This signature then acts as proof that the incoming Alice owns the private key associated with Alice's onion address.
Be very careful when approaching these sorts of problems, rolling your own crypto is almost invariably bad, another reason to use preexisting solutions.
Similarly be very careful writing C++ or any non-memory-safe language.