12 Commits

2034 changed files with 3543 additions and 581696 deletions

2
.gitignore vendored
View File

@@ -8,3 +8,5 @@
/toolchain/sysroot/
/toolchain/build/
.vscode/
check/

View File

@@ -3,13 +3,15 @@ project( snoop_device )
set( CMAKE_CXX_STANDARD 23 )
find_package(OpenSSL REQUIRED)
add_subdirectory( third_party/portaudio )
add_subdirectory( third_party/opus )
add_subdirectory( third_party/cpp-httplib )
add_subdirectory( third_party/nlohmann_json )
add_subdirectory( third_party/spdlog )
add_subdirectory( third_party/libogg )
add_subdirectory( third_party/socket.io-client-cpp )
add_subdirectory( third_party/libdatachannel )
option(USE_ALSA_ADAPTER "Use ALSA Adapter" OFF)
if(USE_ALSA_ADAPTER)
@@ -32,8 +34,25 @@ set( HEADERS
src/Services/AudioStreamService.h
src/Services/AudioWriterService.h
src/Services/ConfigService.h
src/Services/DeviceControlService.h
src/Services/EnrollmentService.h
src/Security/TlsKeyUtil.h
src/Security/SslCertUtil.h
)
add_executable( ${PROJECT_NAME} ${SOURCES} ${HEADERS} )
target_include_directories( ${PROJECT_NAME} PRIVATE src )
target_link_libraries( ${PROJECT_NAME} PRIVATE portaudio_static opus sioclient_tls nlohmann_json spdlog::spdlog_header_only Ogg::ogg httplib::httplib )
# Enable HTTPS (mTLS) support in cpp-httplib across ALL TUs of this target
target_compile_definitions(${PROJECT_NAME} PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries( ${PROJECT_NAME} PRIVATE
portaudio_static
opus
nlohmann_json
spdlog::spdlog_header_only
Ogg::ogg
httplib::httplib
datachannel
OpenSSL::SSL
OpenSSL::Crypto
)

View File

@@ -1,5 +1,7 @@
{
"m_guid": "123e4567-e89b-12d3-a456-426614174000",
"m_recordingDuration": 10000,
"m_baseUrl": "http://localhost:3000"
"m_baseUrl": "http://localhost:3000",
"m_polling": 120,
"m_jitter": 10
}

39
gen_device_csr.sh Normal file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
set -euo pipefail
if [ $# -ne 1 ]; then
echo "Usage: $0 <DEVICE_GUID>"
exit 1
fi
GUID="$1"
# Output files
KEY_FILE="device_${GUID}.key"
CSR_FILE="device_${GUID}.csr"
CONF_FILE="csr_${GUID}.conf"
# Generate config for CSR
cat > "$CONF_FILE" <<EOF
[ req ]
default_md = sha256
prompt = no
distinguished_name = dn
req_extensions = req_ext
[ dn ]
CN = $GUID
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
URI.1 = urn:device:$GUID
EOF
# Generate private key
openssl ecparam -name prime256v1 -genkey -noout -out "$KEY_FILE"
chmod 600 "$KEY_FILE"
# Generate CSR
openssl req -new -key "$KEY_FILE" -out "$CSR_FILE" -config "$CONF_FILE"

11
key-load.service Normal file
View File

@@ -0,0 +1,11 @@
[Unit]
Description=Load IoT TLS key into kernel keyring
After=network-pre.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/load-iot-key.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

26
load-iot-key.sh Normal file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
set -e
CPU_SERIAL=$(awk '/Serial/ {print $3}' /proc/cpuinfo)
# Fallback if CPU_SERIAL is empty
if [[ -z "$CPU_SERIAL" ]]; then
CPU_SERIAL="999999999999"
fi
KEK=$(echo -n "$CPU_SERIAL" | \
openssl dgst -sha256 -hmac "server-provided-salt" | \
awk '{print $2}')
# Decrypt into tmpfs
mkdir -p /run/iot
openssl enc -d -aes-256-gcm -pbkdf2 \
-pass pass:$KEK \
-in /etc/iot/keys/device.key.enc \
-out /run/iot/device.key
# Load into kernel keyring (root-only key)
keyctl padd user iot-client-key @s < /run/iot/device.key
# Securely erase plaintext
shred -u /run/iot/device.key

512
src/Security/SslCertUtil.h Normal file
View File

@@ -0,0 +1,512 @@
// src/Security/SslCertUtil.h
#pragma once
#include <string>
#include <vector>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <sys/stat.h> // chmod
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <spdlog/spdlog.h>
namespace snoop
{
namespace device_sec
{
class SslCertUtil
{
public:
// Generates:
// - EC P-256 private key at keyPath (PEM, chmod 600)
// - CSR at csrPath with:
// CN = guid
// subjectAltName = URI:urn:device:<guid>
static void GenerateEcKeyAndCsr(const std::string &guid,
const std::filesystem::path &keyPath,
const std::filesystem::path &csrPath)
{
spdlog::info("SslCertUtil: generating EC key + CSR for GUID={}", guid);
EVP_PKEY *pkey = nullptr;
EC_KEY *ec = nullptr;
try
{
// ----- 1) Generate EC key (prime256v1) -----
ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (!ec)
{
throw std::runtime_error("EC_KEY_new_by_curve_name failed");
}
if (EC_KEY_generate_key(ec) != 1)
{
throw std::runtime_error("EC_KEY_generate_key failed");
}
pkey = EVP_PKEY_new();
if (!pkey)
{
throw std::runtime_error("EVP_PKEY_new failed");
}
if (EVP_PKEY_assign_EC_KEY(pkey, ec) != 1)
{
throw std::runtime_error("EVP_PKEY_assign_EC_KEY failed");
}
// pkey now owns ec
ec = nullptr; // prevent double free
// ----- 5) Write key + CSR to disk -----
SavePrivateKeyPem(pkey, keyPath);
BuildAndSaveCsr(pkey, guid, csrPath);
spdlog::info("SslCertUtil: key written to '{}', csr written to '{}'",
keyPath.string(), csrPath.string());
}
catch (...)
{
if (pkey)
{
EVP_PKEY_free(pkey);
}
if (ec)
{
EC_KEY_free(ec);
}
throw; // rethrow
}
EVP_PKEY_free(pkey);
}
// CSR from an existing key
static void GenerateCsrFromExistingKey(const std::string &guid,
const std::filesystem::path &keyPath,
const std::filesystem::path &csrPath)
{
spdlog::info("SslCertUtil: generating CSR from existing key '{}'", keyPath.string());
FILE *kf = fopen(keyPath.string().c_str(), "rb");
if (!kf)
{
throw std::runtime_error("GenerateCsrFromExistingKey: cannot open key file: " +
keyPath.string());
}
EVP_PKEY *pkey = PEM_read_PrivateKey(kf, nullptr, nullptr, nullptr);
fclose(kf);
if (!pkey)
{
throw std::runtime_error("GenerateCsrFromExistingKey: PEM_read_PrivateKey failed");
}
try
{
BuildAndSaveCsr(pkey, guid, csrPath);
}
catch (...)
{
EVP_PKEY_free(pkey);
throw;
}
EVP_PKEY_free(pkey);
spdlog::info("SslCertUtil: CSR written to '{}'", csrPath.string());
}
// Encrypt file with AES-256-CBC + PBKDF2 + salt.
//
// Equivalent to:
// openssl enc -aes-256-cbc -pbkdf2 -salt -pass pass:<password> -in in -out out
//
// NOTE: Does NOT shred or delete the input file; caller should do that
// (youll still call `shred -u` from EnrollmentService).
static void EncryptFileAes256CbcPbkdf2(const std::filesystem::path &inPath,
const std::filesystem::path &outPath,
const std::string &password)
{
spdlog::info("SslCertUtil: encrypting '{}' -> '{}' (AES-256-CBC + PBKDF2)",
inPath.string(), outPath.string());
// ----- 1) Read input file -----
std::ifstream fin(inPath, std::ios::binary);
if (!fin)
{
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: cannot open input file: " +
inPath.string());
}
std::vector<unsigned char> plaintext(
(std::istreambuf_iterator<char>(fin)),
std::istreambuf_iterator<char>());
fin.close();
// ----- 2) Prepare salt, key, iv -----
unsigned char salt[8];
if (RAND_bytes(salt, sizeof(salt)) != 1)
{
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: RAND_bytes failed");
}
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
const int keyLen = EVP_CIPHER_key_length(cipher);
const int ivLen = EVP_CIPHER_iv_length(cipher);
std::vector<unsigned char> keyiv(keyLen + ivLen);
const int iterations = 10000; // PBKDF2 rounds
if (PKCS5_PBKDF2_HMAC(password.c_str(),
static_cast<int>(password.size()),
salt, sizeof(salt),
iterations,
EVP_sha256(),
keyLen + ivLen,
keyiv.data()) != 1)
{
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: PKCS5_PBKDF2_HMAC failed");
}
unsigned char *key = keyiv.data();
unsigned char *iv = keyiv.data() + keyLen;
// ----- 3) Encrypt -----
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx)
{
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_CIPHER_CTX_new failed");
}
std::vector<unsigned char> ciphertext(plaintext.size() + EVP_CIPHER_block_size(cipher));
int outLen1 = 0;
int outLen2 = 0;
if (EVP_EncryptInit_ex(ctx, cipher, nullptr, key, iv) != 1)
{
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_EncryptInit_ex failed");
}
if (EVP_EncryptUpdate(ctx,
ciphertext.data(), &outLen1,
plaintext.data(), static_cast<int>(plaintext.size())) != 1)
{
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_EncryptUpdate failed");
}
if (EVP_EncryptFinal_ex(ctx,
ciphertext.data() + outLen1, &outLen2) != 1)
{
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: EVP_EncryptFinal_ex failed");
}
EVP_CIPHER_CTX_free(ctx);
ciphertext.resize(outLen1 + outLen2);
// ----- 4) Write output: [salt][ciphertext] -----
std::ofstream fout(outPath, std::ios::binary | std::ios::trunc);
if (!fout)
{
throw std::runtime_error("EncryptFileAes256CbcPbkdf2: cannot open output file: " +
outPath.string());
}
// Just raw salt followed by ciphertext
fout.write(reinterpret_cast<const char *>(salt), sizeof(salt));
fout.write(reinterpret_cast<const char *>(ciphertext.data()),
static_cast<std::streamsize>(ciphertext.size()));
fout.close();
spdlog::info("SslCertUtil: encryption finished, wrote {}", outPath.string());
}
// Compute HMAC-SHA256 and return lowercase hex string.
// Equivalent to:
// printf %s "<data>" | openssl dgst -sha256 -hmac "<key>" | awk '{print $2}'
static std::string ComputeHmacSha256Hex(const std::string &key,
const std::string &data)
{
unsigned char mac[EVP_MAX_MD_SIZE];
unsigned int macLen = 0;
if (!HMAC(EVP_sha256(),
key.data(), static_cast<int>(key.size()),
reinterpret_cast<const unsigned char *>(data.data()),
data.size(),
mac, &macLen))
{
throw std::runtime_error("ComputeHmacSha256Hex: HMAC(EVP_sha256) failed");
}
return ToHexLower(mac, macLen);
}
// Decrypt file with AES-256-CBC + PBKDF2 + salt.
// Layout: [8-byte salt][ciphertext]
// Password is KEK (hex string from HMAC).
static void DecryptFileAes256CbcPbkdf2(const std::filesystem::path &inPath,
const std::filesystem::path &outPath,
const std::string &password)
{
spdlog::info("SslCertUtil: decrypting '{}' -> '{}' (AES-256-CBC + PBKDF2)",
inPath.string(), outPath.string());
// ----- 1) Read input file -----
std::ifstream fin(inPath, std::ios::binary);
if (!fin)
{
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: cannot open input file: " +
inPath.string());
}
std::vector<unsigned char> enc(
(std::istreambuf_iterator<char>(fin)),
std::istreambuf_iterator<char>());
fin.close();
if (enc.size() < 8)
{
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: encrypted file too small");
}
// First 8 bytes = salt, rest = ciphertext
unsigned char salt[8];
std::copy(enc.begin(), enc.begin() + 8, salt);
std::vector<unsigned char> ciphertext(enc.begin() + 8, enc.end());
// ----- 2) Derive key/iv -----
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
const int keyLen = EVP_CIPHER_key_length(cipher);
const int ivLen = EVP_CIPHER_iv_length(cipher);
std::vector<unsigned char> keyiv(keyLen + ivLen);
const int iterations = 10000;
if (PKCS5_PBKDF2_HMAC(password.c_str(),
static_cast<int>(password.size()),
salt, sizeof(salt),
iterations,
EVP_sha256(),
keyLen + ivLen,
keyiv.data()) != 1)
{
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: PKCS5_PBKDF2_HMAC failed");
}
unsigned char *key = keyiv.data();
unsigned char *iv = keyiv.data() + keyLen;
// ----- 3) Decrypt -----
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx)
{
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_CIPHER_CTX_new failed");
}
std::vector<unsigned char> plaintext(ciphertext.size() + EVP_CIPHER_block_size(cipher));
int outLen1 = 0;
int outLen2 = 0;
if (EVP_DecryptInit_ex(ctx, cipher, nullptr, key, iv) != 1)
{
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_DecryptInit_ex failed");
}
if (EVP_DecryptUpdate(ctx,
plaintext.data(), &outLen1,
ciphertext.data(), static_cast<int>(ciphertext.size())) != 1)
{
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_DecryptUpdate failed");
}
if (EVP_DecryptFinal_ex(ctx,
plaintext.data() + outLen1, &outLen2) != 1)
{
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: EVP_DecryptFinal_ex failed");
}
EVP_CIPHER_CTX_free(ctx);
plaintext.resize(outLen1 + outLen2);
// ----- 4) Write plaintext -----
std::filesystem::create_directories(outPath.parent_path());
std::ofstream fout(outPath, std::ios::binary | std::ios::trunc);
if (!fout)
{
throw std::runtime_error("DecryptFileAes256CbcPbkdf2: cannot open output file: " +
outPath.string());
}
fout.write(reinterpret_cast<const char *>(plaintext.data()),
static_cast<std::streamsize>(plaintext.size()));
fout.close();
spdlog::info("SslCertUtil: decryption finished, wrote {}", outPath.string());
}
private:
static void BuildAndSaveCsr(EVP_PKEY *pkey,
const std::string &guid,
const std::filesystem::path &csrPath)
{
X509_REQ *req = X509_REQ_new();
if (!req)
{
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_new failed");
}
try
{
// Subject: CN = GUID
X509_NAME *name = X509_NAME_new();
if (!name)
{
throw std::runtime_error("BuildAndSaveCsr: X509_NAME_new failed");
}
if (X509_NAME_add_entry_by_NID(
name,
NID_commonName,
MBSTRING_ASC,
reinterpret_cast<const unsigned char *>(guid.c_str()),
-1, -1, 0) != 1)
{
X509_NAME_free(name);
throw std::runtime_error("BuildAndSaveCsr: X509_NAME_add_entry_by_NID(CN) failed");
}
if (X509_REQ_set_subject_name(req, name) != 1)
{
X509_NAME_free(name);
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_set_subject_name failed");
}
X509_NAME_free(name);
// Public key
if (X509_REQ_set_pubkey(req, pkey) != 1)
{
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_set_pubkey failed");
}
// subjectAltName = URI:urn:device:<GUID>
X509V3_CTX ctx;
X509V3_set_ctx_nodb(&ctx);
X509V3_set_ctx(&ctx, nullptr, nullptr, req, nullptr, 0);
std::string sanStr = "URI:urn:device:" + guid;
X509_EXTENSION *ext = X509V3_EXT_conf_nid(
nullptr, &ctx, NID_subject_alt_name,
const_cast<char *>(sanStr.c_str()));
if (!ext)
{
throw std::runtime_error("BuildAndSaveCsr: X509V3_EXT_conf_nid(subjectAltName) failed");
}
STACK_OF(X509_EXTENSION) *exts = sk_X509_EXTENSION_new_null();
if (!exts)
{
X509_EXTENSION_free(ext);
throw std::runtime_error("BuildAndSaveCsr: sk_X509_EXTENSION_new_null failed");
}
sk_X509_EXTENSION_push(exts, ext);
if (X509_REQ_add_extensions(req, exts) != 1)
{
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_add_extensions failed");
}
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
// Sign with SHA-256
if (X509_REQ_sign(req, pkey, EVP_sha256()) <= 0)
{
throw std::runtime_error("BuildAndSaveCsr: X509_REQ_sign failed");
}
SaveCsrPem(req, csrPath);
}
catch (...)
{
X509_REQ_free(req);
throw;
}
X509_REQ_free(req);
}
static void SavePrivateKeyPem(EVP_PKEY *pkey, const std::filesystem::path &keyPath)
{
const auto parent = keyPath.parent_path();
if (!parent.empty())
{
std::filesystem::create_directories(parent);
}
FILE *f = fopen(keyPath.string().c_str(), "wb");
if (!f)
{
throw std::runtime_error("SavePrivateKeyPem: fopen failed: " + keyPath.string());
}
if (PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr) != 1)
{
fclose(f);
throw std::runtime_error("SavePrivateKeyPem: PEM_write_PrivateKey failed");
}
fclose(f);
// chmod 600
::chmod(keyPath.string().c_str(), 0600);
}
static void SaveCsrPem(X509_REQ *req, const std::filesystem::path &csrPath)
{
const auto parent = csrPath.parent_path();
if (!parent.empty())
{
std::filesystem::create_directories(parent);
}
FILE *f = fopen(csrPath.string().c_str(), "wb");
if (!f)
{
throw std::runtime_error("SaveCsrPem: fopen failed: " + csrPath.string());
}
if (PEM_write_X509_REQ(f, req) != 1)
{
fclose(f);
throw std::runtime_error("SaveCsrPem: PEM_write_X509_REQ failed");
}
fclose(f);
}
static std::string ToHexLower(const unsigned char *buf, size_t len)
{
static const char *hex = "0123456789abcdef";
std::string out;
out.reserve(len * 2);
for (size_t i = 0; i < len; ++i)
{
unsigned char b = buf[i];
out.push_back(hex[b >> 4]);
out.push_back(hex[b & 0x0F]);
}
return out;
}
};
} // namespace device_sec
} // namespace snoop

241
src/Security/TlsKeyUtil.h Normal file
View File

@@ -0,0 +1,241 @@
#pragma once
#include <filesystem>
#include <string>
#include <array>
#include <vector>
#include <stdexcept>
#include <cstdio>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <spdlog/spdlog.h>
#include "SslCertUtil.h"
namespace snoop
{
namespace device_sec
{
// --- helpers ---
static std::string Trim(const std::string &s)
{
auto b = s.find_first_not_of(" \t\r\n");
auto e = s.find_last_not_of(" \t\r\n");
if (b == std::string::npos)
{
return "";
}
return s.substr(b, e - b + 1);
}
static std::string Exec(const std::string &cmd)
{
std::array<char, 4096> buf{};
std::string out;
FILE *pipe = popen((cmd + " 2>&1").c_str(), "r");
if (!pipe)
{
throw std::runtime_error("popen failed: " + cmd);
}
while (fgets(buf.data(), (int)buf.size(), pipe) != nullptr)
{
out.append(buf.data());
}
int rc = pclose(pipe);
int exitCode = WIFEXITED(rc) ? WEXITSTATUS(rc) : rc;
if (exitCode != 0)
{
spdlog::warn("Command '{}' exited with code {}", cmd, exitCode);
}
return out;
}
// dumps the client key from keyring to a temp file and returns its path
static std::filesystem::path ExtractClientKeyFromKernelKeyring()
{
std::string id = Trim(Exec("keyctl search @s user iot-client-key | tail -n1"));
if (id.empty())
{
throw std::runtime_error("iot-client-key not found in keyring");
}
// Create a secure temp file
char tmpl[] = "/run/iot/iot-keyXXXXXX";
int fd = mkstemp(tmpl);
if (fd < 0)
{
throw std::runtime_error("mkstemp failed for client key");
}
close(fd);
std::filesystem::path p(tmpl);
// Pipe the key payload into the temp file
std::string cmd = "keyctl pipe " + id + " > " + p.string();
Exec(cmd);
// quick sanity
if (std::filesystem::file_size(p) == 0)
{
std::error_code ec;
std::filesystem::remove(p, ec);
throw std::runtime_error("keyctl pipe produced empty client key");
}
return p;
}
struct TempFile
{
std::filesystem::path path;
int fd{-1};
explicit TempFile(const std::filesystem::path &dir, const char *pattern = "iot-keyXXXXXX")
{
std::string tmpl = (dir / pattern).string();
std::vector<char> name(tmpl.begin(), tmpl.end());
name.push_back('\0');
fd = mkstemp(name.data());
if (fd < 0)
{
throw std::runtime_error("mkstemp failed");
}
fchmod(fd, S_IRUSR | S_IWUSR);
path = name.data();
}
void write_all(const void *data, size_t n)
{
const uint8_t *p = static_cast<const uint8_t *>(data);
size_t off = 0;
while (off < n)
{
ssize_t w = ::write(fd, p + off, n - off);
if (w <= 0)
{
throw std::runtime_error("write failed");
}
off += (size_t)w;
}
fsync(fd);
}
~TempFile()
{
if (fd >= 0)
{
::close(fd);
}
std::error_code ec;
std::filesystem::remove(path, ec);
}
};
inline std::vector<uint8_t> ReadClientKeyPayloadFromKeyring()
{
// 1) get key id
std::string id = Trim(Exec("keyctl search @s user iot-client-key | tail -n1"));
if (id.empty())
{
throw std::runtime_error("iot-client-key not found in keyring");
}
// 2) capture payload (no redirection to file)
std::string bytes = Exec("keyctl pipe " + id);
if (bytes.empty())
{
throw std::runtime_error("keyctl pipe returned empty payload");
}
return std::vector<uint8_t>(bytes.begin(), bytes.end());
}
static std::string GetCpuSerial()
{
std::ifstream f("/proc/cpuinfo");
if (!f)
{
spdlog::warn("GetCpuSerial: /proc/cpuinfo not available, using fallback");
return std::string(12, '9');
}
std::string line;
std::regex re(R"(^\s*Serial\s*:\s*([0-9A-Fa-f]+)\s*$)");
while (std::getline(f, line))
{
std::smatch m;
if (std::regex_match(line, m, re) && m.size() == 2)
{
auto serial = Trim(m[1].str());
if (!serial.empty())
{
return serial;
}
}
}
spdlog::warn("GetCpuSerial: Serial not found, using fallback");
return std::string(12, '9');
}
inline bool IsClientKeyInKernelKeyring()
{
std::string id = Trim(Exec("keyctl search @s user iot-client-key | tail -n1"));
return !id.empty();
}
// Ensure that iot-client-key exists in kernel keyring.
// - If already present: no-op, returns false (nothing loaded).
// - If not present: decrypts /etc/iot/keys/device.key.enc using KEK
// derived from (GUID, CPU_SERIAL), padds into keyring, returns true.
inline bool EnsureClientKeyInKernelKeyring(const std::string &guid)
{
if (IsClientKeyInKernelKeyring())
{
spdlog::info("EnsureClientKeyInKernelKeyring: key already present, nothing to do");
return false;
}
const std::filesystem::path encPath = "/etc/iot/keys/device.key.enc";
if (!std::filesystem::exists(encPath))
{
throw std::runtime_error("EnsureClientKeyInKernelKeyring: encrypted key not found at " +
encPath.string());
}
// Derive KEK = HMAC-SHA256(cpuSerial, key=GUID)
const std::string cpuSerial = GetCpuSerial();
spdlog::info("EnsureClientKeyInKernelKeyring: CPU_SERIAL = {}", cpuSerial);
const std::string kek =
snoop::device_sec::SslCertUtil::ComputeHmacSha256Hex(guid, cpuSerial);
if (kek.empty())
{
throw std::runtime_error("EnsureClientKeyInKernelKeyring: empty KEK");
}
spdlog::debug("EnsureClientKeyInKernelKeyring: KEK (hex) = {}", kek);
// Decrypt into tmpfs: /run/iot/device.key
const std::filesystem::path runDir = "/run/iot";
const std::filesystem::path plainPath = runDir / "device.key";
std::filesystem::create_directories(runDir);
snoop::device_sec::SslCertUtil::DecryptFileAes256CbcPbkdf2(
encPath,
plainPath,
kek);
if (!std::filesystem::exists(plainPath))
{
throw std::runtime_error("EnsureClientKeyInKernelKeyring: decrypted key not created");
}
// Load into kernel keyring
const std::string cmd = "keyctl padd user iot-client-key @s < " + plainPath.string();
spdlog::info("EnsureClientKeyInKernelKeyring: loading key into kernel keyring...");
auto out = Exec(cmd);
spdlog::debug("keyctl padd output:\n{}", out);
// Securely erase plaintext
const std::string shredCmd = "shred -u " + plainPath.string();
spdlog::info("EnsureClientKeyInKernelKeyring: shredding plaintext key with '{}'", shredCmd);
auto shredOut = Exec(shredCmd);
spdlog::debug("shred output:\n{}", shredOut);
return true;
}
}
}

View File

@@ -1,102 +1,104 @@
// src/Services/AudioStreamService.h
#pragma once
#include <sio_client.h>
#include <atomic>
#include <spdlog/spdlog.h>
#include <chrono>
#include <filesystem>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <cstdio>
#include <array>
namespace snoop {
#include <spdlog/spdlog.h>
class AudioStreamService {
std::shared_ptr<sio::client> m_client;
std::string m_guid;
std::atomic<bool> m_isConnected = false;
std::atomic<bool> m_isInStreaming = false;
std::vector<char> m_audioBuffer;
std::mutex m_bufferMutex;
const unsigned long long int FLUSH_PERIOD = 5000;
const std::vector<char> PACKET_DELIMITER = {
static_cast<char>(0xFF),
static_cast<char>(0xFE),
static_cast<char>(0xFD),
static_cast<char>(0xFC)
#include "WhipClient.h"
#include "ConfigService.h"
#include "Security/TlsKeyUtil.h"
namespace snoop
{
class AudioStreamService
{
std::shared_ptr<ConfigService> m_cfg;
// WHIP
std::unique_ptr<WhipClient> m_whip;
std::mutex m_whipMutex;
public:
explicit AudioStreamService(std::shared_ptr<ConfigService> cfg)
: m_cfg(std::move(cfg)) {}
~AudioStreamService() { StopWhip(); }
// Feed encoded Opus; frames = PCM samples per channel represented by this Opus frame
void OnOpus(const unsigned char *opusData, size_t opusBytes, int pcmFramesPerChannel)
{
std::lock_guard lk(m_whipMutex);
if (m_whip)
{
m_whip->PushOpus(opusData, opusBytes, pcmFramesPerChannel);
}
}
bool StartWhip(const std::string &whipUrl, int sampleRate = 48000, int channels = 1)
{
std::lock_guard lk(m_whipMutex);
if (m_whip)
{
spdlog::info("WHIP already started");
return true;
}
if (!m_cfg)
{
spdlog::error("StartWhip requires ConfigService");
return false;
}
// certs from enrollment
std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem";
if (!std::filesystem::exists(ca))
{
ca = "/etc/iot/keys/ca_chain.pem";
}
std::filesystem::path crt = "/etc/iot/keys/device.crt.pem";
WhipClient::Params p{
.whipUrl = whipUrl, // may include ?token=..., client will normalize path and set Bearer
.caPath = ca.string(),
.crtPath = crt.string(),
.sampleRate = sampleRate,
.channels = channels};
m_whip = std::make_unique<WhipClient>(p);
try
{
m_whip->Start();
spdlog::info("WHIP started");
return true;
}
catch (const std::exception &e)
{
spdlog::error("WHIP start failed: {}", e.what());
m_whip.reset();
return false;
}
}
void StopWhip()
{
std::lock_guard lk(m_whipMutex);
if (m_whip)
{
m_whip->Stop();
m_whip.reset();
}
}
};
unsigned long long int m_flushedAt = 0;
public:
explicit AudioStreamService( std::shared_ptr<sio::client> client, std::string guid ) :
m_client( std::move( client ) ),
m_guid( std::move( guid ) ) {
SetupEventListeners();
}
~AudioStreamService() {
this->m_isConnected = false;
this->m_isInStreaming = false;
}
void SendAudioData( const char* input, size_t size ) {
if( !this->m_isConnected || !this->m_isInStreaming ) {
return;
}
std::lock_guard lock( m_bufferMutex );
this->m_audioBuffer.insert(m_audioBuffer.end(), PACKET_DELIMITER.begin(), PACKET_DELIMITER.end());
this->m_audioBuffer.insert( m_audioBuffer.end(), input, input + size );
auto now = std::chrono::system_clock::now();
auto currentTime = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
if( currentTime >= this->m_flushedAt + FLUSH_PERIOD ) {
FlushBuffer();
}
}
private:
void SetupEventListeners() {
this->m_client->set_open_listener( [this]() {
spdlog::info( "Connected to server" );
this->m_client->socket( "/livestream" )->emit( "register_device", m_guid );
this->m_isConnected = true;
} );
this->m_client->set_close_listener( [this]( sio::client::close_reason const& reason ) {
this->m_isConnected = false;
this->m_isInStreaming = false;
spdlog::info( "Disconnected from server" );
} );
this->m_client->set_fail_listener( []() {
spdlog::info( "Failed to connect to server" );
} );
this->m_client->socket( "/livestream" )->on( "start_streaming", [this]( sio::event& ev ) {
spdlog::info( "Start streaming command received" );
this->m_isInStreaming = true;
auto now = std::chrono::system_clock::now();
this->m_flushedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
} );
this->m_client->socket( "/livestream" )->on( "stop_streaming", [this]( sio::event& ev ) {
spdlog::info( "Stop streaming command received" );
this->m_isInStreaming = false;
std::lock_guard lock( this->m_bufferMutex );
this->m_audioBuffer.clear();
} );
}
void FlushBuffer() {
if( this->m_audioBuffer.empty() ) {
return;
}
this->m_client->socket( "/livestream" )->emit( "audio_data",
std::make_shared<std::string>( this->m_audioBuffer.data(), this->m_audioBuffer.size() )
);
this->m_audioBuffer.clear();
auto now = std::chrono::system_clock::now();
this->m_flushedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
}
};
}
} // namespace snoop

View File

@@ -5,177 +5,508 @@
#include <thread>
#include <atomic>
#include <chrono>
#include <httplib.h>
#include <mutex>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <array>
#include <optional>
#include <spdlog/spdlog.h>
#include <httplib.h> // build with CPPHTTPLIB_OPENSSL_SUPPORT
#include <sys/wait.h>
#include "AudioWriters/OggAudioWriter.h"
#include "ConfigService.h"
#include "Security/TlsKeyUtil.h"
namespace snoop
{
namespace snoop {
class AudioWriterService {
class AudioWriterService
{
std::shared_ptr<ConfigService> m_configService;
std::shared_ptr<OggAudioWriter> m_oggWriter;
std::string m_destinationDirectoryPath;
std::string m_queueDirectoryPath;
std::thread m_writingThread;
std::thread m_uploadThread;
std::mutex m_fetchFilePathsMutex;
std::mutex m_stateMutex;
unsigned long long int m_currentRecordStartedAt = 0;
std::string m_currentRecordFilePath;
std::atomic<bool> m_isIntermission = false;
public:
explicit AudioWriterService( std::shared_ptr<ConfigService> configService, std::string destinationDirectoryPath ) :
m_configService( std::move( configService ) ),
m_destinationDirectoryPath( std::move( destinationDirectoryPath ) ),
m_oggWriter( std::make_shared<OggAudioWriter>( 48000, 1 ) ) {
if( !this->m_destinationDirectoryPath.empty() && !this->m_destinationDirectoryPath.ends_with( "/" ) ) {
this->m_destinationDirectoryPath.append( "/" );
// New recording control flags
std::atomic<bool> m_recordingEnabled{false};
std::atomic<bool> m_stopAfterCurrentSegment{false};
public:
explicit AudioWriterService(std::shared_ptr<ConfigService> configService, std::string destinationDirectoryPath)
: m_configService(std::move(configService)),
m_destinationDirectoryPath(std::move(destinationDirectoryPath)),
m_oggWriter(std::make_shared<OggAudioWriter>(48000, 1))
{
if (!this->m_destinationDirectoryPath.empty() && !this->m_destinationDirectoryPath.ends_with("/"))
{
this->m_destinationDirectoryPath.append("/");
}
this->m_queueDirectoryPath = this->m_destinationDirectoryPath + "queue/";
std::filesystem::create_directories( this->m_queueDirectoryPath );
std::filesystem::create_directories(this->m_queueDirectoryPath);
this->MoveToQueueUncompletedRecords();
this->m_writingThread = std::thread( [this]() {
this->WritingThread();
} );
this->m_uploadThread = std::thread( [this]() {
this->UploadThread();
} );
spdlog::info( "AudioWriterService::AudioWriterService()" );
// Start background threads
this->m_writingThread = std::thread([this]()
{ this->WritingThread(); });
this->m_uploadThread = std::thread([this]()
{ this->UploadThread(); });
spdlog::info("AudioWriterService constructed; initial recordingEnabled=false");
}
void WriteAudioData( const char* data, size_t size, size_t frames ) {
this->m_oggWriter->Write( data, size, frames );
}
~AudioWriterService() {
~AudioWriterService()
{
this->m_isIntermission = true;
if (this->m_writingThread.joinable())
{
this->m_writingThread.join();
}
if (this->m_uploadThread.joinable())
{
this->m_uploadThread.join();
}
}
private:
void MoveToQueueUncompletedRecords() {
// -------- Public control API (called from DeviceControlService handlers) --------
// Begin a new recording cycle immediately (creates a fresh segment and starts writing).
void StartRecording()
{
std::lock_guard<std::mutex> lk(this->m_stateMutex);
if (m_recordingEnabled.load())
{
spdlog::info("StartRecording ignored: already recording");
return;
}
m_stopAfterCurrentSegment = false;
m_recordingEnabled = true;
spdlog::info("Recording enabled");
}
// Graceful stop: finish the current segment at the next rotation boundary, then stop.
void StopRecordingGracefully()
{
std::lock_guard<std::mutex> lk(this->m_stateMutex);
if (!m_recordingEnabled.load())
{
spdlog::info("StopRecordingGracefully ignored: not recording");
return;
}
m_stopAfterCurrentSegment = true;
spdlog::info("Recording will stop after current segment completes");
}
bool IsRecording() const
{
return m_recordingEnabled.load();
}
// Called from the encoder callback — only writes when recording is enabled
void WriteAudioData(const char *data, size_t size, size_t frames)
{
if (!m_recordingEnabled.load())
{
return;
}
this->m_oggWriter->Write(data, size, frames);
}
// stops recording immidiately, when deep_sleep received
void StopRecordingNow()
{
std::lock_guard<std::mutex> lk(this->m_stateMutex);
if (!m_recordingEnabled.load())
{
spdlog::info("StopRecordingNow ignored: not recording");
return;
}
auto stoppedAtMs = NowMs();
// Force-close current segment right away, enqueue, and disable recording.
this->m_oggWriter->StopWriting();
this->MoveToUploadQueue(this->m_currentRecordFilePath,this->m_currentRecordStartedAt,stoppedAtMs);
m_recordingEnabled = false;
m_stopAfterCurrentSegment = false;
spdlog::info("Recording stopped immediately (deep sleep)");
}
private:
// ----------------------- Helpers (HTTPS mTLS) -----------------------
struct Url
{
std::string scheme;
std::string host;
int port = 0;
};
std::unique_ptr<httplib::SSLClient> m_uploadClient;
std::string m_uploadHost;
int m_uploadPort = 0;
bool m_uploadHttps = true;
std::filesystem::path m_uploadCa;
std::filesystem::path m_uploadCrt;
static Url ParseBase(const std::string &base)
{
std::regex re(R"(^\s*(https?)://([^/:]+)(?::(\d+))?\s*$)");
std::smatch m;
if (!std::regex_match(base, m, re))
{
throw std::runtime_error("Invalid base URL: " + base);
}
Url u;
u.scheme = m[1].str();
u.host = m[2].str();
u.port = m[3].matched ? std::stoi(m[3].str()) : (u.scheme == "https" ? 443 : 80);
return u;
}
std::unique_ptr<httplib::SSLClient> MakeClientMTLS(const Url &u,
const std::filesystem::path &ca,
const std::filesystem::path &crt,
const std::filesystem::path &key)
{
if (u.scheme == "https")
{
auto cli = std::make_unique<httplib::SSLClient>(u.host.c_str(), u.port, crt.string().c_str(), key.string().c_str(), std::string());
cli->enable_server_certificate_verification(false);
cli->set_ca_cert_path(ca.string().c_str());
cli->set_connection_timeout(10);
cli->set_read_timeout(120);
cli->set_write_timeout(120);
return cli;
}
}
bool EnsureUploadClient()
{
// Recompute endpoint pieces
const auto baseUrl = this->m_configService->GetBaseUrl();
Url url = ParseBase(baseUrl);
// Detect changes requiring re-init
bool needReinit =
!m_uploadClient ||
m_uploadHost != url.host || m_uploadPort != url.port ||
(url.scheme == "https") != m_uploadHttps;
// Setup CA/CRT (from enrollment)
std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem";
if (!std::filesystem::exists(ca))
{
ca = "/etc/iot/keys/ca_chain.pem";
}
const std::filesystem::path crt = "/etc/iot/keys/device.crt.pem";
if (!needReinit)
{
// still ok — reuse
return true;
}
try
{
auto payload = snoop::device_sec::ReadClientKeyPayloadFromKeyring();
// pick a writable dir (use /tmp to avoid /run root perms surprises)
snoop::device_sec::TempFile tf(std::filesystem::temp_directory_path());
tf.write_all(payload.data(), payload.size());
// OpenSSL loads the files now; once this returns, the key is in-memory.
auto cli = MakeClientMTLS(url, ca, crt, tf.path);
// tf destructor will remove the file at scope end
// Swap in
m_uploadClient = std::move(cli);
m_uploadHost = url.host;
m_uploadPort = url.port;
m_uploadHttps = (url.scheme == "https");
m_uploadCa = ca;
m_uploadCrt = crt;
return true;
}
catch (const std::exception &e)
{
spdlog::error("EnsureUploadClient: failed to set client cert: {}", e.what());
return false;
}
}
// ----------------------------- Existing logic (adjusted) -----------------------------
void MoveToQueueUncompletedRecords()
{
std::vector<std::filesystem::path> files;
for( const auto& entry: std::filesystem::directory_iterator( this->m_destinationDirectoryPath ) ) {
files.push_back( entry.path() );
for (const auto &entry : std::filesystem::directory_iterator(this->m_destinationDirectoryPath))
{
files.push_back(entry.path());
}
for( const auto& file: files ) {
if( file.filename().string() != "queue" ) {
spdlog::info( "Move uncompleted record {} to queue", file.filename().string() );
this->MoveToUploadQueue( file.string() );
for (const auto &file : files)
{
if (file.filename().string() != "queue")
{
spdlog::info("Move uncompleted record {} to queue", file.filename().string());
this->MoveToUploadQueue(file.string());
}
}
}
void WritingThread() {
auto now = std::chrono::system_clock::now();
this->m_currentRecordStartedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
this->m_currentRecordFilePath = this->m_destinationDirectoryPath + std::to_string( this->m_currentRecordStartedAt );
this->m_oggWriter->StartWriting( this->m_currentRecordFilePath );
void WritingThread()
{
constexpr unsigned long long DEFAULT_SEGMENT_MS = 30'000; // 30 seconds
while (!m_isIntermission)
{
if (!m_recordingEnabled.load())
{
std::this_thread::sleep_for(std::chrono::milliseconds(200));
continue;
}
while( !m_isIntermission ) {
now = std::chrono::system_clock::now();
auto currentRecordDuration = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count() -
this->m_currentRecordStartedAt;
if( currentRecordDuration >= this->m_configService->GetRecordingDuration() ) {
// --- Get segment duration from config ---
unsigned long long segDurationMs = this->m_configService->GetRecordingDuration();
if (segDurationMs < 1000) // anything < 1s is basically useless here
{
spdlog::warn("RecordingDuration={} ms is too small, using default {} ms",
segDurationMs, DEFAULT_SEGMENT_MS);
segDurationMs = DEFAULT_SEGMENT_MS;
}
spdlog::info("Starting new segment with target duration={} ms", segDurationMs);
// --- Mark start times (wall + monotonic) ---
auto wallStart = std::chrono::system_clock::now();
this->m_currentRecordStartedAt =
std::chrono::duration_cast<std::chrono::milliseconds>(wallStart.time_since_epoch()).count();
this->m_currentRecordFilePath =
this->m_destinationDirectoryPath + std::to_string(this->m_currentRecordStartedAt);
this->m_oggWriter->StartWriting(this->m_currentRecordFilePath);
spdlog::info("Recording segment started: {}", this->m_currentRecordFilePath);
auto monoStart = std::chrono::steady_clock::now();
const auto targetDuration = std::chrono::milliseconds(segDurationMs);
// --- Wait until segment duration elapses ---
while (!m_isIntermission && m_recordingEnabled.load())
{
auto elapsed = std::chrono::steady_clock::now() - monoStart;
if (elapsed >= targetDuration)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
auto wallStop = std::chrono::system_clock::now();
auto stoppedAtMs =
std::chrono::duration_cast<std::chrono::milliseconds>(wallStop.time_since_epoch()).count();
// --- Close and enqueue ---
this->m_oggWriter->StopWriting();
this->MoveToUploadQueue( this->m_currentRecordFilePath );
this->m_currentRecordStartedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
this->m_currentRecordFilePath = this->m_destinationDirectoryPath + std::to_string( this->m_currentRecordStartedAt );
this->m_oggWriter->StartWriting( this->m_currentRecordFilePath );
this->MoveToUploadQueue(this->m_currentRecordFilePath,
this->m_currentRecordStartedAt,
stoppedAtMs);
spdlog::info("Recording segment finished: {} ({} ms)",
this->m_currentRecordFilePath,
stoppedAtMs - this->m_currentRecordStartedAt);
// If graceful stop requested, stop after finishing this segment
if (m_stopAfterCurrentSegment.load())
{
m_recordingEnabled = false;
m_stopAfterCurrentSegment = false;
spdlog::info("Recording disabled after graceful stop");
}
std::this_thread::sleep_for( std::chrono::milliseconds( 1000 ) );
}
// Clean shutdown if we exit while still recording
if (m_recordingEnabled.load())
{
auto wallStop = std::chrono::system_clock::now();
auto stoppedAtMs =
std::chrono::duration_cast<std::chrono::milliseconds>(wallStop.time_since_epoch()).count();
this->m_oggWriter->StopWriting();
this->MoveToUploadQueue( this->m_currentRecordFilePath );
// TODO: Move to upload queue
this->MoveToUploadQueue(this->m_currentRecordFilePath,
this->m_currentRecordStartedAt,
stoppedAtMs);
m_recordingEnabled = false;
}
void MoveToUploadQueue( const std::string& filePath ) {
spdlog::info( "AudioWriterService::MoveToUploadQueue( {} )", filePath );
std::lock_guard lock( this->m_fetchFilePathsMutex );
}
// Helper: ms since epoch
static unsigned long long NowMs()
{
auto now = std::chrono::system_clock::now();
auto recordStoppedAt = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count();
if( std::filesystem::exists( filePath ) ) {
auto fileName = std::filesystem::path( filePath ).filename().string() + "-" + std::to_string( recordStoppedAt );
std::filesystem::rename( filePath, m_queueDirectoryPath + fileName );
return std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
}
void MoveToUploadQueue(const std::string &filePath, unsigned long long startedAt, unsigned long long stoppedAt)
{
spdlog::info("MoveToUploadQueue( {} )", filePath);
std::lock_guard lock(this->m_fetchFilePathsMutex);
if (std::filesystem::exists(filePath))
{
auto fileName = std::to_string(startedAt) + "-" + std::to_string(stoppedAt);
std::filesystem::rename(filePath, m_queueDirectoryPath + fileName);
}
}
void UploadThread() {
while (!m_isIntermission) {
// Legacy helper for startup recovery
void MoveToUploadQueue(const std::string &filePath)
{
const auto stoppedAt = NowMs();
unsigned long long startedAt = 0;
auto base = std::filesystem::path(filePath).filename().string();
try
{
startedAt = std::stoull(base);
}
catch (...)
{
// if filename is not pure number, fall back to stoppedAt
startedAt = stoppedAt;
}
MoveToUploadQueue(filePath, startedAt, stoppedAt);
}
void UploadThread()
{
const auto baseUrl = this->m_configService->GetBaseUrl();
Url url = ParseBase(baseUrl);
// certs from enrollment
std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem";
if (!std::filesystem::exists(ca))
{
ca = "/etc/iot/keys/ca_chain.pem";
}
const std::filesystem::path crt = "/etc/iot/keys/device.crt.pem";
while (!m_isIntermission)
{
std::vector<std::filesystem::path> files;
{
std::lock_guard l(this->m_fetchFilePathsMutex);
try {
for (const auto& entry : std::filesystem::directory_iterator(this->m_queueDirectoryPath)) {
try
{
for (const auto &entry : std::filesystem::directory_iterator(this->m_queueDirectoryPath))
{
if (entry.is_regular_file())
{
files.push_back(entry.path());
}
} catch (const std::exception& e) {
}
}
catch (const std::exception &e)
{
spdlog::error("Error reading queue directory: {}", e.what());
}
}
for (const auto& filePath : files) {
try
{
if (!EnsureUploadClient())
{
std::this_thread::sleep_for(std::chrono::seconds(1));
continue;
}
for (const auto &filePath : files)
{
auto fileName = filePath.filename().string();
spdlog::info("Processing file: {}", fileName);
spdlog::info("Uploading file: {}", fileName);
size_t delimiterPos = fileName.find('-');
if (delimiterPos != std::string::npos) {
if (delimiterPos == std::string::npos)
{
spdlog::warn("Unexpected filename format, skipping: {}", fileName);
continue;
}
std::string startedAt = fileName.substr(0, delimiterPos);
std::string stoppedAt = fileName.substr(delimiterPos + 1);
try {
spdlog::info("Attempting to upload file...");
if (SendRecordedFile(filePath.string(), stoull(startedAt), stoull(stoppedAt))) {
try
{
if (SendRecordedFileMTLS(*m_uploadClient, filePath.string(),
std::stoull(startedAt),
std::stoull(stoppedAt)))
{
spdlog::info("File uploaded, deleting: {}", filePath.string());
std::filesystem::remove(filePath);
} else {
}
else
{
spdlog::warn("Failed to upload file: {}", filePath.string());
}
} catch (const std::exception& e) {
}
catch (const std::exception &e)
{
spdlog::error("Exception during file upload: {}", e.what());
m_uploadClient.reset();
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
catch (const std::exception &e)
{
spdlog::error("mTLS client setup failed: {}", e.what());
}
std::this_thread::sleep_for(std::chrono::milliseconds(800));
}
}
bool SendRecordedFile( const std::string& filepath, unsigned long long int startedAt, unsigned long long stoppedAt ) {
spdlog::info( "SendRecordedFile: {}", filepath );
httplib::Client client( this->m_configService->GetBaseUrl() );
std::ifstream ifs( filepath, std::ios::binary );
if( !ifs ) {
throw std::runtime_error( "Failed to open file" );
bool SendRecordedFileMTLS(httplib::SSLClient &client,
const std::string &filepath,
unsigned long long int startedAt,
unsigned long long int stoppedAt)
{
spdlog::info("SendRecordedFile (mTLS): {}", filepath);
std::ifstream ifs(filepath, std::ios::binary);
if (!ifs)
{
throw std::runtime_error("Failed to open file: " + filepath);
}
std::vector<char> buffer((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
std::vector<char> buffer( ( std::istreambuf_iterator<char>( ifs ) ), ( std::istreambuf_iterator<char>() ) );
auto res = client.Post(
std::string( "/records/upload/" ),
httplib::MultipartFormDataItems{
{ "file", std::string( buffer.begin(), buffer.end() ), "file.ogg", "audio/ogg" },
{ "guid", this->m_configService->GetGuid() },
{ "startedAt", std::to_string( startedAt ) },
{ "stoppedAt", std::to_string( stoppedAt ) },
} );
// Multipart form: file + guid + times (same fields as before)
const std::string guid = this->m_configService->GetGuid();
httplib::MultipartFormDataItems items = {
{"file", std::string(buffer.begin(), buffer.end()), "file.ogg", "audio/ogg"},
{"guid", guid, "", "text/plain"},
{"startedAt", std::to_string(startedAt), "", "text/plain"},
{"stoppedAt", std::to_string(stoppedAt), "", "text/plain"},
};
if( res && res->status == 201 ) {
spdlog::info( "File uploaded successfully" );
auto res = client.Post("/api/records/upload", items);
if (res && (res->status == 201 || res->status == 200))
{
spdlog::info("File uploaded successfully: HTTP {}", res->status);
return true;
}
spdlog::error( "Failed to upload file" );
if (res)
{
spdlog::error("Upload failed: HTTP {}, body: {}", res->status, res->body);
}
else
{
spdlog::error("Upload failed: no response");
}
return false;
}
};
};
}
} // namespace snoop

View File

@@ -8,69 +8,110 @@
#include <fstream>
#include <sstream>
namespace snoop {
namespace snoop
{
class Config {
public:
class Config
{
public:
std::string m_guid;
unsigned long long m_recordingDuration = 0;
std::string m_baseUrl;
};
unsigned int m_polling = 120;
unsigned int m_jitter = 10;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( Config, m_guid, m_recordingDuration, m_baseUrl )
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Config, m_guid, m_recordingDuration, m_baseUrl, m_polling, m_jitter)
class ConfigService {
class ConfigService
{
std::shared_ptr<Config> m_config;
std::string m_configFilePath;
std::mutex m_mutex;
public:
explicit ConfigService( const std::string& configFilePath ) :
m_configFilePath( configFilePath ) {
if( !std::filesystem::exists( this->m_configFilePath ) ) {
throw std::runtime_error( std::string( "ConfigService: Config not found " ) + this->m_configFilePath );
public:
explicit ConfigService(const std::string &configFilePath) : m_configFilePath(configFilePath)
{
if (!std::filesystem::exists(this->m_configFilePath))
{
throw std::runtime_error(std::string("ConfigService: Config not found ") + this->m_configFilePath);
}
std::string configFileContent = this->ReadFile( this->m_configFilePath );
nlohmann::json jsonConfig = nlohmann::json::parse( configFileContent );
this->m_config = std::make_shared<Config>( jsonConfig.get<Config>() );
std::string configFileContent = this->ReadFile(this->m_configFilePath);
nlohmann::json jsonConfig = nlohmann::json::parse(configFileContent);
this->m_config = std::make_shared<Config>(jsonConfig.get<Config>());
}
[[nodiscard]] std::string GetGuid() const {
[[nodiscard]] std::string GetGuid() const
{
return this->m_config->m_guid;
}
[[nodiscard]] unsigned long long int GetRecordingDuration() const {
[[nodiscard]] unsigned long long int GetRecordingDuration() const
{
return this->m_config->m_recordingDuration;
}
void SetRecordingDuration( unsigned long long int recordingDuration ) {
void SetRecordingDuration(unsigned long long int recordingDuration)
{
this->m_config->m_recordingDuration = recordingDuration;
this->RewriteConfig();
}
[[nodiscard]] std::string GetBaseUrl() const {
[[nodiscard]] std::string GetBaseUrl() const
{
return this->m_config->m_baseUrl;
}
private:
std::string ReadFile( const std::string& path ) {
void SetBaseUrl(std::string newBaseUrl)
{
this->m_config->m_baseUrl = newBaseUrl;
this->RewriteConfig();
}
[[nodiscard]] unsigned int GetPollingInterwall()
{
return this->m_config->m_polling;
}
void SetPollingInterwall(unsigned int newPollInterwall)
{
this->m_config->m_polling = newPollInterwall;
this->RewriteConfig();
}
[[nodscard]] unsigned int GetJitter()
{
return this->m_config->m_jitter;
}
void SetJitter(unsigned int newJitter)
{
this->m_config->m_jitter = newJitter;
this->RewriteConfig();
}
private:
std::string ReadFile(const std::string &path)
{
std::fstream f;
f.open( path, std::ios::in );
f.open(path, std::ios::in);
std::stringstream ss;
ss << f.rdbuf();
return ss.str();
}
void RewriteFile( const std::string& path, const std::string& fileContent ) {
void RewriteFile(const std::string &path, const std::string &fileContent)
{
std::fstream f;
f.open( path, std::ios::out );
f.open(path, std::ios::out);
f << fileContent;
f.close();
}
void RewriteConfig() {
this->RewriteFile( this->m_configFilePath, nlohmann::json( *this->m_config ).dump() );
void RewriteConfig()
{
this->RewriteFile(this->m_configFilePath, nlohmann::json(*this->m_config).dump());
}
};
};
}

View File

@@ -0,0 +1,605 @@
#pragma once
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <string>
#include <string_view>
#include <vector>
#include <map>
#include <thread>
#include <atomic>
#include <random>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <optional>
#include <regex>
#include <array>
#include <spdlog/spdlog.h>
#include <nlohmann/json.hpp>
#include <httplib.h> // build with CPPHTTPLIB_OPENSSL_SUPPORT
#include <sys/wait.h> // for WEXITSTATUS
#include "ConfigService.h"
#include "Security/TlsKeyUtil.h"
namespace snoop
{
class DeviceControlService
{
public:
struct Task
{
uint64_t id{};
std::string type;
nlohmann::json payload = nlohmann::json::object();
};
// Handler returns: {success, result_json_string, error_message}
using HandlerResult = std::tuple<bool, std::string, std::string>;
using TaskHandler = std::function<HandlerResult(const Task &)>;
struct Handlers
{
// Fill any you implement. Unset => default “not implemented”.
TaskHandler onStartStream;
TaskHandler onStopStream;
TaskHandler onStartRecording;
TaskHandler onStopRecording;
TaskHandler onUpdateConfig;
TaskHandler onSetDeepSleep;
TaskHandler onRenewCert;
};
struct Controls
{
std::function<void()> stopStreamNow; // e.g., streamer.StopWhip()
std::function<void()> stopRecordingNow; // e.g., writerService->StopRecordingNow()
};
DeviceControlService(std::shared_ptr<ConfigService> cfg, Handlers handlers, Controls controls)
: m_cfg(std::move(cfg)), m_handlers(std::move(handlers)), m_controls(std::move(controls))
{
m_stop = false;
m_thread = std::thread(&DeviceControlService::RunLoop, this);
}
~DeviceControlService()
{
m_stop = true;
if (m_thread.joinable())
{
m_thread.join();
}
}
void ArmDeepSleep(long long startMs, long long stopMs)
{
// Record sleep-until, and if were already inside the window, act now.
const auto now = NowMs();
if (stopMs <= startMs)
{
spdlog::warn("ArmDeepSleep: stop<=start");
return;
}
// If already inside window -> enter immediately
if (now >= startMs && now < stopMs)
{
this->EnterDeepSleepUntil(stopMs);
}
else
{
// Otherwise, set a future "until" and the loop will enter at startMs.
// Well keep both values:
m_sleepStartMs = startMs;
m_sleepUntilMs = stopMs;
}
}
static std::optional<long long> ParseRfc3339UtcToMs(const std::string &s)
{
// Minimal, robust-ish parser for "YYYY-MM-DDTHH:MM:SS[.frac]Z"
// For production, consider date::parse or a small RFC3339 lib.
std::tm tm{};
char z = 'Z';
double frac = 0.0;
int n = 0;
// accept with optional fractional seconds
if (sscanf(s.c_str(), "%d-%d-%dT%d:%d:%d%n", &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n) != 6)
{
return std::nullopt;
}
tm.tm_year -= 1900;
tm.tm_mon -= 1;
const char *p = s.c_str() + n;
if (*p == '.')
{
// consume fractional
++p;
while (isdigit(*p)) {
++p; // ignore exact fraction
}
}
if (*p != 'Z')
{
return std::nullopt;
}
time_t tt = timegm(&tm);
if (tt < 0)
{
return std::nullopt;
}
return (long long)tt * 1000LL;
}
static long long NowMs()
{
using namespace std::chrono;
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
private:
std::shared_ptr<ConfigService> m_cfg;
Handlers m_handlers;
std::thread m_thread;
std::atomic<bool> m_stop{false};
std::atomic<long long> m_sleepUntilMs{0}; // epoch ms; 0 = not sleeping
std::atomic<long long> m_sleepStartMs{0};
Controls m_controls;
struct Url
{
std::string scheme; // http/https
std::string host; // hostname or ip
int port = 0; // default if 0
};
std::unique_ptr<httplib::SSLClient> m_taskClient;
std::string m_taskHost;
int m_taskPort = 0;
bool m_taskHttps = true;
std::filesystem::path m_taskCa;
std::filesystem::path m_taskCrt;
static Url ParseBase(const std::string &base)
{
// very small parser: scheme://host[:port]
std::regex re(R"(^\s*(https?)://([^/:]+)(?::(\d+))?\s*$)");
std::smatch m;
if (!std::regex_match(base, m, re))
{
throw std::runtime_error("Invalid base URL for DeviceControlService: " + base);
}
Url u;
u.scheme = m[1].str();
u.host = m[2].str();
u.port = m[3].matched ? std::stoi(m[3].str()) : (u.scheme == "https" ? 443 : 80);
return u;
}
static std::optional<long long> JsonTimeToMs(const nlohmann::json &j)
{
if (j.contains("start_ms") && j.contains("stop_ms"))
{
return j.at("stop_ms").get<long long>(); // caller will use start_ms separately
}
if (j.contains("start") && j.contains("stop") && j["start"].is_string() && j["stop"].is_string())
{
auto sMs = ParseRfc3339UtcToMs(j["start"].get<std::string>());
auto eMs = ParseRfc3339UtcToMs(j["stop"].get<std::string>());
if (sMs && eMs)
{
return *eMs; // caller will use start separately
}
}
if (j.contains("start") && j["start"].is_string() &&
j["start"].get<std::string>() == "now" && j.contains("for_s"))
{
long long forS = j["for_s"].get<long long>();
return NowMs() + forS * 1000LL;
}
return std::nullopt;
}
// dumps the client key from keyring to a temp file and returns its path
// Create HTTPS client configured for mTLS (or HTTP if base is http)
std::unique_ptr<httplib::SSLClient> MakeClient(const Url &u,
const std::filesystem::path &ca,
const std::filesystem::path &crt,
const std::filesystem::path &key)
{
if (u.scheme == "https")
{
auto cli = std::make_unique<httplib::SSLClient>(u.host.c_str(), u.port, crt.string().c_str(), key.string().c_str(), std::string());
cli->enable_server_certificate_verification(false);
cli->set_ca_cert_path(ca.string().c_str());
cli->set_connection_timeout(10);
cli->set_read_timeout(60);
cli->set_write_timeout(60);
if (!cli->is_valid())
{
spdlog::warn("[mTLS debug] SSLClient is not valid right after creation.");
}
else
{
spdlog::debug("[mTLS debug] SSLClient initialized OK for host={} port={}", u.host, u.port);
}
return cli;
}
}
// simple jittered sleep
void SleepWithJitterOnce(std::mt19937 &rng, int baseSec, int jitterSec)
{
if (baseSec < 0)
{
baseSec = 0;
}
if (jitterSec < 0)
{
jitterSec = 0;
}
std::uniform_int_distribution<int> dist(-jitterSec, +jitterSec);
int delay = baseSec + dist(rng);
if (delay < 0)
{
delay = 0;
}
std::this_thread::sleep_for(std::chrono::seconds(delay));
}
// default fallback if handler is not provided
static HandlerResult NotImplemented(const Task &t)
{
std::string msg = "Handler not implemented for type: " + (t.type.empty() ? "<empty>" : t.type);
spdlog::warn("{}", msg);
// Include echo of payload for debugging
nlohmann::json res = {
{"type", t.type},
{"payload", t.payload}};
return {false, res.dump(), msg};
}
TaskHandler ResolveHandler(std::string_view type) const
{
if (type == "start_stream")
return m_handlers.onStartStream ? m_handlers.onStartStream : NotImplemented;
if (type == "stop_stream")
return m_handlers.onStopStream ? m_handlers.onStopStream : NotImplemented;
if (type == "start_recording")
return m_handlers.onStartRecording ? m_handlers.onStartRecording : NotImplemented;
if (type == "stop_recording")
return m_handlers.onStopRecording ? m_handlers.onStopRecording : NotImplemented;
if (type == "update_config")
return m_handlers.onUpdateConfig ? m_handlers.onUpdateConfig : NotImplemented;
if (type == "set_deep_sleep")
return m_handlers.onSetDeepSleep ? m_handlers.onSetDeepSleep : NotImplemented;
if (type == "renew_cert")
return m_handlers.onRenewCert ? m_handlers.onRenewCert : NotImplemented;
return NotImplemented;
}
static std::vector<Task> ParseTasks(const std::string &body)
{
std::vector<Task> out;
nlohmann::json j = nlohmann::json::parse(body);
auto to_id = [](const nlohmann::json &v) -> uint64_t
{
if (v.is_number_unsigned())
{
return v.get<uint64_t>();
}
if (v.is_number_integer())
{
return static_cast<uint64_t>(v.get<long long>());
}
if (v.is_string())
{
try
{
return static_cast<uint64_t>(std::stoull(v.get<std::string>()));
}
catch (...)
{
}
}
return 0ULL;
};
auto parse_one = [&](const nlohmann::json &x)
{
Task t;
if (x.contains("id"))
{
t.id = to_id(x["id"]);
}
if (x.contains("type") && x["type"].is_string())
{
t.type = x["type"].get<std::string>();
}
// payload can be stringified JSON or object
try
{
if (x.contains("payload"))
{
const auto &raw = x.at("payload");
if (raw.is_string())
{
try
{
t.payload = nlohmann::json::parse(raw.get<std::string>());
}
catch (...)
{
t.payload = raw.get<std::string>();
}
}
else if (raw.is_object() || raw.is_array())
{
t.payload = raw;
}
else
{
t.payload = nlohmann::json::object();
}
}
}
catch (...)
{
t.payload = nlohmann::json::object();
}
if (t.id == 0)
{
spdlog::warn("Task without valid id: {}", x.dump());
}
if (t.type.empty())
{
spdlog::warn("Task without type: {}", x.dump());
}
out.push_back(std::move(t));
};
if (j.is_array())
{
for (const auto &it : j)
parse_one(it);
}
else if (j.is_object())
{
// NEW: wrapper shape { hasTask: bool, task: { ... } }
if (j.contains("task") && j["task"].is_object())
{
// If server includes a 'status' gate and you only want runnable tasks, you can check here:
// if (j.value("hasTask", false) == true)
parse_one(j["task"]);
}
else
{
// bare single task
parse_one(j);
}
}
else
{
spdlog::warn("Unexpected tasks JSON: {}", body);
}
return out;
}
void PostResult(httplib::SSLClient &cli, const std::string &guid,
uint64_t taskId, bool success,
const std::string &resultJson, const std::string &err)
{
if (taskId == 0)
{
spdlog::warn("Skip posting result: invalid taskId=0 (success={}, err={})", success, err);
return;
}
nlohmann::json dto = {
{"taskId", taskId},
{"success", success},
{"result", resultJson.empty() ? "{}" : resultJson},
{"error", err}};
std::string path = "/api/tasks/" + guid;
auto body = dto.dump();
spdlog::debug("POST {} body: {}", path, body);
auto res = cli.Post(path.c_str(), body, "application/json");
if (!res)
{
spdlog::error("POST {} failed (no response)", path);
return;
}
spdlog::info("POST {} -> HTTP {} body: {}", path, res->status, res->body);
}
void EnterDeepSleepUntil(long long stopMs)
{
m_sleepStartMs = NowMs();
m_sleepUntilMs = stopMs;
spdlog::info("Entering deep sleep until {} ms", stopMs);
// Stop stream & recording immediately
if (m_controls.stopStreamNow)
{
m_controls.stopStreamNow();
}
if (m_controls.stopRecordingNow)
{
m_controls.stopRecordingNow();
}
}
bool EnsureTaskClient()
{
// Recompute endpoint pieces
const auto baseUrl = this->m_cfg->GetBaseUrl();
Url url = ParseBase(baseUrl);
bool needReinit = !m_taskClient ||
m_taskHost != url.host || m_taskPort != url.port ||
(url.scheme == "https") != m_taskHttps;
// Setup CA/CRT (from enrollment)
std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem";
if (!std::filesystem::exists(ca))
ca = "/etc/iot/keys/ca_chain.pem";
const std::filesystem::path crt = "/etc/iot/keys/device.crt.pem";
if (!needReinit)
{
// still ok — reuse
return true;
}
try
{
auto payload = snoop::device_sec::ReadClientKeyPayloadFromKeyring();
// pick a writable dir (use /tmp to avoid /run root perms surprises)
snoop::device_sec::TempFile tf(std::filesystem::temp_directory_path());
tf.write_all(payload.data(), payload.size());
// OpenSSL loads the files now; once this returns, the key is in-memory.
auto cli = MakeClient(url, ca, crt, tf.path);
spdlog::info("Creating new client because of change");
// tf destructor will remove the file at scope end
// Swap in
m_taskClient = std::move(cli);
m_taskHost = url.host;
m_taskPort = url.port;
m_taskHttps = (url.scheme == "https");
m_taskCa = ca;
m_taskCrt = crt;
return true;
}
catch (const std::exception &e)
{
spdlog::error("EnsureTaskClient: failed to set client cert: {}", e.what());
return false;
}
}
void RunLoop()
{
const std::string guid = m_cfg->GetGuid();
std::mt19937 rng{std::random_device{}()};
while (!m_stop)
{
// (1) Ensure we have a reusable client
if (!EnsureTaskClient())
{
spdlog::info("Sleep after task client");
SleepWithJitterOnce(rng, m_cfg->GetPollingInterwall(), m_cfg->GetJitter());
continue;
}
//// FOR SSL DEBUG - remove in prod
if (m_taskClient)
{
long verify_result = m_taskClient->get_openssl_verify_result();
if (verify_result == X509_V_OK)
{
spdlog::info("[mTLS debug] OpenSSL verify: SUCCESS (X509_V_OK)");
}
else
{
const char *err_str = X509_verify_cert_error_string(verify_result);
spdlog::error("[mTLS debug] OpenSSL verify failed: {} ({})",
err_str ? err_str : "Unknown error", verify_result);
}
}
/////////////////////////////////
// (2) Deep-sleep gate
const auto now = NowMs();
const auto startMs = m_sleepStartMs.load();
const auto untilMs = m_sleepUntilMs.load();
if (untilMs > 0)
{
if (now < startMs)
{
// not yet sleeping: wait until start, no requests
auto waitMs = std::min<long long>(startMs - now, 60'000);
std::this_thread::sleep_for(std::chrono::milliseconds(waitMs));
continue;
}
if (now >= startMs && now < untilMs)
{
// sleeping: no requests; ensure we stopped stream/recording on entry
if (now - startMs < 1200)
{
if (m_controls.stopStreamNow)
m_controls.stopStreamNow();
if (m_controls.stopRecordingNow)
m_controls.stopRecordingNow();
}
auto waitMs = std::min<long long>(untilMs - now, 60'000);
std::this_thread::sleep_for(std::chrono::milliseconds(waitMs));
continue;
}
if (now >= untilMs)
{
// wake up
m_sleepStartMs = 0;
m_sleepUntilMs = 0;
spdlog::info("Deep sleep finished; resuming operation");
}
}
// (3) Not sleeping -> poll tasks
try
{
const std::string path = "/api/tasks/" + guid;
spdlog::info("GET {}", path);
auto res = m_taskClient->Get(path.c_str());
if (!res)
{
spdlog::warn("GET {} failed (no response)", path);
m_taskClient.reset(); // force rebuild next iter
}
else if (res->status == 204)
{
spdlog::debug("No tasks (204).");
}
else if (res->status >= 200 && res->status < 300)
{
auto tasks = ParseTasks(res->body);
for (const auto &t : tasks)
{
auto handler = ResolveHandler(t.type);
auto [ok, resultJson, err] = handler(t);
if (t.id == 0)
{
spdlog::warn("Won't POST result for task with id=0: type='{}'", t.type);
continue;
}
PostResult(*m_taskClient, guid, t.id, ok, resultJson, err);
}
}
else
{
spdlog::warn("GET {} -> HTTP {}, body: {}", path, res->status, res->body);
}
}
catch (const std::exception &e)
{
spdlog::error("Task loop error: {}", e.what());
m_taskClient.reset();
}
// (4) Backoff before next poll
SleepWithJitterOnce(rng, m_cfg->GetPollingInterwall(), m_cfg->GetJitter());
}
}
};
} // namespace snoop

View File

@@ -0,0 +1,715 @@
// src/Services/EnrollmentService.h
#pragma once
#include <string>
#include <vector>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <regex>
#include <memory>
#include <spdlog/spdlog.h>
#include <httplib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/asn1.h>
#include "ConfigService.h"
#include "Security/SslCertUtil.h"
#include "Security/TlsKeyUtil.h"
namespace snoop
{
class EnrollmentService
{
std::shared_ptr<ConfigService> m_cfg;
static std::string Exec(const std::string &cmd)
{
std::array<char, 4096> buf{};
std::string out;
FILE *pipe = popen((cmd + " 2>&1").c_str(), "r");
if (!pipe)
{
throw std::runtime_error("popen failed: " + cmd);
}
while (fgets(buf.data(), (int)buf.size(), pipe) != nullptr)
{
out.append(buf.data());
}
auto rc = pclose(pipe);
int exitCode = WIFEXITED(rc) ? WEXITSTATUS(rc) : rc;
if (exitCode != 0)
{
spdlog::warn("Command '{}' exited with code {}", cmd, exitCode);
}
return out;
}
static void WriteFile(const std::filesystem::path &p, const std::string &data, bool createParents = true)
{
if (createParents)
{
std::filesystem::create_directories(p.parent_path());
}
std::ofstream f(p, std::ios::binary);
if (!f)
{
throw std::runtime_error("Cannot open file for write: " + p.string());
}
f.write(data.data(), (std::streamsize)data.size());
}
static std::string ReadFile(const std::filesystem::path &p)
{
std::ifstream f(p, std::ios::binary);
if (!f)
{
throw std::runtime_error("Cannot open file for read: " + p.string());
}
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
static std::string Trim(const std::string &s)
{
auto b = s.find_first_not_of(" \t\r\n");
auto e = s.find_last_not_of(" \t\r\n");
if (b == std::string::npos)
return "";
return s.substr(b, e - b + 1);
}
static std::string ParseCpuSerial()
{
// Default for “empty” case: 12 times '9'
std::string fallback(12, '9');
std::ifstream f("/proc/cpuinfo");
if (!f)
{
spdlog::warn("/proc/cpuinfo not available, using fallback serial");
return fallback;
}
std::string line;
std::regex re(R"(^\s*Serial\s*:\s*([0-9A-Fa-f]+)\s*$)");
while (std::getline(f, line))
{
std::smatch m;
if (std::regex_match(line, m, re) && m.size() == 2)
{
auto serial = Trim(m[1].str());
if (!serial.empty())
{
return serial;
}
}
}
spdlog::warn("CPU Serial not found, using fallback");
return fallback;
}
public:
explicit EnrollmentService(std::shared_ptr<ConfigService> cfg) : m_cfg(std::move(cfg)) {}
// Returns true if enrollment was performed now; false if it was already done
bool EnsureEnrolled()
{
const std::string guid = m_cfg->GetGuid();
const std::filesystem::path keystoreDir = "/etc/iot/keys";
const auto encKeyPath = keystoreDir / "device.key.enc";
const auto certPath = keystoreDir / "device.crt.pem";
const auto caPath = keystoreDir / "issuing_ca.pem";
const auto chainPath = keystoreDir / "ca_chain.pem";
// If we already have encrypted key + cert, assume done
if (std::filesystem::exists(encKeyPath) &&
std::filesystem::exists(certPath))
{
spdlog::info("Enrollment already completed.");
return false;
}
spdlog::info("Starting first-run enrollment...");
// // 1) Run gen_device_csr.sh <GUID>
// {
// // Assumes script is in the working dir or in PATH
// const std::string cmd = "bash ./gen_device_csr.sh " + guid;
// spdlog::info("Executing: {}", cmd);
// auto out = Exec(cmd);
// spdlog::debug("gen_device_csr.sh output:\n{}", out);
// }
const std::string keyName = "device_" + guid + ".key";
const std::string csrName = "device_" + guid + ".csr";
// if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
// {
// throw std::runtime_error("CSR or key was not generated by gen_device_csr.sh");
// }
// 1) Generate key + CSR via OpenSSL (no external script)
// {
// spdlog::info("Generating device key + CSR via SslCertUtil");
// snoop::device_sec::SslCertUtil::GenerateEcKeyAndCsr(
// guid,
// std::filesystem::path(keyName),
// std::filesystem::path(csrName));
// }
// if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
// {
// throw std::runtime_error("CSR or key was not generated");
// }
// 1) CPU serial (awk '/Serial/ {print $3}' /proc/cpuinfo), if empty -> 9 * 12
const std::string cpuSerial = ParseCpuSerial();
spdlog::info("CPU_SERIAL = {}", cpuSerial);
// 3) KEK = HMAC-SHA256(cpuSerial, key=GUID) (bash equiv used)
// std::string kek;
// {
// // careful to avoid trailing newline
// const std::string cmd = "printf %s \"" + cpuSerial + "\""
// " | openssl dgst -sha256 -hmac \"" +
// guid + "\" | awk '{print $2}'";
// spdlog::info("Deriving KEK with HMAC-SHA256");
// kek = Trim(Exec(cmd));
// if (kek.empty())
// throw std::runtime_error("Failed to derive KEK");
// spdlog::debug("KEK (hex) = {}", kek);
// }
// 2) KEK = HMAC-SHA256(cpuSerial, key=GUID)
std::string kek;
{
spdlog::info("Deriving KEK with HMAC-SHA256 (OpenSSL API)");
kek = snoop::device_sec::SslCertUtil::ComputeHmacSha256Hex(guid, cpuSerial);
if (kek.empty())
{
throw std::runtime_error("Failed to derive KEK");
}
spdlog::debug("KEK (hex) = {}", kek);
}
// // 4) Encrypt the private key to /etc/iot/keys/device.key.enc using KEK; shred original
// {
// std::filesystem::create_directories(keystoreDir);
// const std::string cmd =
// "openssl enc -aes-256-cbc -pbkdf2 -salt "
// "-pass pass:" +
// kek + " "
// "-in " +
// keyName + " "
// "-out " +
// encKeyPath.string() + " "
// "&& shred -u " +
// keyName;
// spdlog::info("Encrypting private key and shredding plaintext...");
// auto out = Exec(cmd);
// spdlog::debug("openssl enc output:\n{}", out);
// if (!std::filesystem::exists(encKeyPath))
// {
// throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
// }
// }
// 3) Generate key + CSR via OpenSSL (no external script)
if (!std::filesystem::exists(encKeyPath))
{
// ---- Fresh key: generate + encrypt ----
spdlog::info("No encrypted key yet -> generating new EC key + CSR");
snoop::device_sec::SslCertUtil::GenerateEcKeyAndCsr(
guid,
std::filesystem::path(keyName),
std::filesystem::path(csrName));
if (!std::filesystem::exists(keyName) || !std::filesystem::exists(csrName))
{
throw std::runtime_error("CSR or key was not generated");
}
std::filesystem::create_directories(keystoreDir);
spdlog::info("Encrypting private key via SslCertUtil (AES-256-CBC + PBKDF2)...");
snoop::device_sec::SslCertUtil::EncryptFileAes256CbcPbkdf2(
std::filesystem::path(keyName),
encKeyPath,
kek);
if (!std::filesystem::exists(encKeyPath))
{
throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
}
// shred plaintext key
const std::string shredCmd = "shred -u " + keyName;
spdlog::info("Shredding plaintext private key with '{}'", shredCmd);
auto out = Exec(shredCmd);
spdlog::debug("shred output:\n{}", out);
}
else
{
// ---- Key already exists: reuse it ----
spdlog::info("Encrypted key already exists -> reusing key, regenerating CSR only");
// Decrypt to temp file (in /run/iot)
snoop::device_sec::TempFile tf(std::filesystem::path("/run/iot"));
snoop::device_sec::SslCertUtil::DecryptFileAes256CbcPbkdf2(
encKeyPath,
tf.path,
kek);
// Generate CSR from existing key
snoop::device_sec::SslCertUtil::GenerateCsrFromExistingKey(
guid,
tf.path,
std::filesystem::path(csrName));
if (!std::filesystem::exists(csrName))
{
throw std::runtime_error("CSR was not generated from existing key");
}
// TempFile destructor will securely delete the decrypted key file
}
// // 4) Encrypt the private key to /etc/iot/keys/device.key.enc using KEK; shred original
// {
// std::filesystem::create_directories(keystoreDir);
// spdlog::info("Encrypting private key via SslCertUtil (AES-256-CBC + PBKDF2)...");
// snoop::device_sec::SslCertUtil::EncryptFileAes256CbcPbkdf2(
// std::filesystem::path(keyName),
// encKeyPath,
// kek);
// if (!std::filesystem::exists(encKeyPath))
// {
// throw std::runtime_error("Encrypted key not created: " + encKeyPath.string());
// }
// // still use shred to securely remove plaintext key, as requested
// const std::string shredCmd = "shred -u " + keyName;
// spdlog::info("Shredding plaintext private key with '{}'", shredCmd);
// auto out = Exec(shredCmd);
// spdlog::debug("shred output:\n{}", out);
// }
// 4.5) Ensure key is loaded into kernel keyring (first creation)
try
{
bool loaded = snoop::device_sec::EnsureClientKeyInKernelKeyring(guid);
if (loaded)
{
spdlog::info("EnsureEnrolled: client key loaded into kernel keyring");
}
else
{
spdlog::info("EnsureEnrolled: client key was already in keyring");
}
}
catch (const std::exception &e)
{
spdlog::warn("EnsureEnrolled: failed to load key into keyring: {}", e.what());
}
// 5) Send CSR to /enroll/:guid as multipart form, field `json: {"csr":"<...>"}`
std::string csrPem = ReadFile(csrName);
{
httplib::Client cli(m_cfg->GetBaseUrl());
cli.enable_server_certificate_verification(false);
httplib::MultipartFormDataItems items = {
// name, content, filename, content_type
{"csr", csrPem, "request.csr", "text/plain"}
// or "application/pkcs10" if you prefer: "application/pkcs10"
};
const std::string path = "/api/enroll/" + guid;
spdlog::info("POST {} (multipart)", path);
auto res = cli.Post(path.c_str(), items);
if (!res)
{
throw std::runtime_error("Enroll request failed (no response)");
}
spdlog::info("Enroll response status: {}", res->status);
if (res->status != 200 && res->status != 201)
{
spdlog::info("Enroll error: " + res->body);
throw std::runtime_error("Enroll failed: HTTP " + std::to_string(res->status));
}
// 6) Expect JSON: { "certificate": "...", "issuing_ca":"...", "ca_chain":"..." }
nlohmann::json j;
try
{
j = nlohmann::json::parse(res->body);
}
catch (const std::exception &e)
{
throw std::runtime_error(std::string("Enroll: invalid JSON: ") + e.what());
}
if (!j.contains("certificate") || !j["certificate"].is_string())
{
throw std::runtime_error("Enroll response missing or invalid 'certificate'");
}
WriteFile(certPath, j["certificate"].get<std::string>());
if (j.contains("issuing_ca"))
{
if (!j["issuing_ca"].is_string())
{
throw std::runtime_error("'issuing_ca' must be a string PEM");
}
WriteFile(caPath, j["issuing_ca"].get<std::string>());
}
if (j.contains("ca_chain"))
{
std::string chainPem;
if (j["ca_chain"].is_string())
{
chainPem = j["ca_chain"].get<std::string>();
}
else if (j["ca_chain"].is_array())
{
chainPem = JoinPemArray(j["ca_chain"]);
}
else
{
throw std::runtime_error("'ca_chain' must be string or array of strings");
}
// If server returned both issuing_ca and ca_chain that doesn't include it,
// you can choose to prepend issuing_ca to the chain file:
// if (std::filesystem::exists(caPath)) {
// chainPem = ReadFileToString(caPath) + (chainPem.size() && chainPem.front()!='\n' ? "\n" : "") + chainPem;
// }
WriteFile(chainPath, chainPem);
}
}
// (Optional) cleanup CSR after successful enrollment
// try { std::filesystem::remove(csrName); } catch (...) {}
return true;
}
// Renew cert either because it's about to expire (force=false) or
// because server commanded us (force=true).
// Returns true if renew was done and new files were written.
bool RenewCertificate(bool force = false)
{
const std::string guid = m_cfg->GetGuid();
const std::filesystem::path keystoreDir = "/etc/iot/keys";
const auto certPath = keystoreDir / "device.crt.pem";
const auto caPath = keystoreDir / "issuing_ca.pem";
const auto chainPath = keystoreDir / "ca_chain.pem";
// ----- 1) expiry check (case 1) -----
if (!force)
{
if (!std::filesystem::exists(certPath))
{
spdlog::warn("RenewCertificate: cert not found, skip (not forced)");
return false;
}
if (!IsCertExpiring(certPath, 1))
{
spdlog::info("RenewCertificate: cert is fine, skip");
return false;
}
spdlog::info("RenewCertificate: cert expiring -> will renew");
}
else
{
spdlog::info("RenewCertificate: forced renew");
}
// ----- 2) re-generate CSR via your bash script -----
// {
// const std::string cmd = "bash ./gen_device_csr.sh " + guid;
// spdlog::info("Executing (renew): {}", cmd);
// auto out = Exec(cmd);
// spdlog::debug("gen_device_csr.sh (renew) output:\n{}", out);
// }
// const std::string csrName = "device_" + guid + ".csr";
// if (!std::filesystem::exists(csrName))
// {
// throw std::runtime_error("Renew: CSR was not generated");
// }
// std::string csrPem = ReadFile(csrName);
// ----- 2) re-generate CSR via OpenSSL (no bash) -----
const std::string csrName = "device_" + guid + ".csr";
{
spdlog::info("RenewCertificate: generating new CSR via SslCertUtil");
// We must reuse the existing key or generate a new one?
// Current flow (bash script) always regenerated key+csr, so we mimic that:
snoop::device_sec::SslCertUtil::GenerateEcKeyAndCsr(
guid,
std::filesystem::path("device_" + guid + ".key"),
std::filesystem::path(csrName));
}
if (!std::filesystem::exists(csrName))
{
throw std::runtime_error("Renew: CSR was not generated");
}
std::string csrPem = ReadFile(csrName);
// ----- 3) build mTLS client using current device cert + keyring key -----
const std::string base = m_cfg->GetBaseUrl();
auto pu = ParseBaseUrl(base);
auto cli = MakeMtlsClient(pu);
// ----- 4) send multipart to /api/renew/:guid -----
const std::string path = "/api/renew/" + guid;
httplib::MultipartFormDataItems items = {
{"csr", csrPem, "request.csr", "text/plain"}};
spdlog::info("POST {} (multipart, mTLS)", path);
auto res = cli->Post(path.c_str(), items);
if (!res)
{
throw std::runtime_error("Renew request failed (no response)");
}
spdlog::info("Renew response status: {}", res->status);
if (res->status != 200)
{
throw std::runtime_error("Renew failed: HTTP " + std::to_string(res->status));
}
nlohmann::json j = nlohmann::json::parse(res->body);
if (!j.contains("certificate") || !j["certificate"].is_string())
{
throw std::runtime_error("Renew response missing or invalid 'certificate'");
}
WriteFile(certPath, j["certificate"].get<std::string>());
if (j.contains("issuing_ca") && j["issuing_ca"].is_string())
{
WriteFile(caPath, j["issuing_ca"].get<std::string>());
}
if (j.contains("ca_chain"))
{
std::string chainPem;
if (j["ca_chain"].is_string())
{
chainPem = j["ca_chain"].get<std::string>();
}
else if (j["ca_chain"].is_array())
{
chainPem = JoinPemArray(j["ca_chain"]);
}
else
{
throw std::runtime_error("'ca_chain' must be string or array");
}
WriteFile(chainPath, chainPem);
}
if (j.contains("old_serial") && j["old_serial"].is_string())
{
spdlog::info("Renew: server says old_serial={}", j["old_serial"].get<std::string>());
}
spdlog::info("RenewCertificate: success");
return true;
}
private:
// very small helper to pack PEM into JSON string value (single-line, escaped quotes & newlines)
static std::string EscapeJsonForOneLine(const std::string &in)
{
std::string out;
out.reserve(in.size() * 2);
for (char c : in)
{
switch (c)
{
case '\\':
out += "\\\\";
break;
case '\"':
out += "\\\"";
break;
case '\n':
out += "\\n";
break;
case '\r': /* skip */
break;
default:
out += c;
break;
}
}
return out;
}
static std::string ReadFileToString(const std::string &path)
{
std::ifstream f(path, std::ios::binary);
if (!f)
{
throw std::runtime_error("Failed to open file: " + path);
}
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
static std::string JoinPemArray(const nlohmann::json &arr)
{
// arr is expected to be an array of strings; we join with a single '\n'
std::string out;
for (size_t i = 0; i < arr.size(); ++i)
{
const auto &v = arr.at(i);
if (!v.is_string())
{
throw std::runtime_error("ca_chain element is not string");
}
if (!out.empty() && out.back() != '\n')
{
out.push_back('\n');
}
out += v.get<std::string>();
if (out.back() != '\n')
{
out.push_back('\n');
}
}
return out;
}
static bool IsCertExpiring(const std::filesystem::path &certPath, int daysThreshold)
{
// we will use raw OpenSSL to read the PEM and check notAfter
FILE *f = fopen(certPath.string().c_str(), "r");
if (!f)
{
spdlog::warn("IsCertExpiring: cannot open cert {}", certPath.string());
return false;
}
X509 *cert = PEM_read_X509(f, nullptr, nullptr, nullptr);
fclose(f);
if (!cert)
{
spdlog::warn("IsCertExpiring: cannot parse X509 from {}", certPath.string());
return false;
}
const ASN1_TIME *notAfter = X509_get0_notAfter(cert);
if (!notAfter)
{
spdlog::warn("IsCertExpiring: no notAfter in cert");
X509_free(cert);
return false;
}
// current time
time_t now_t = time(nullptr);
// convert ASN1_TIME → time_t diff
int days = 0, secs = 0;
// OpenSSL has ASN1_TIME_diff in newer versions
if (ASN1_TIME_diff(&days, &secs, nullptr, notAfter) == 0)
{
spdlog::warn("IsCertExpiring: ASN1_TIME_diff failed");
X509_free(cert);
return false;
}
X509_free(cert);
// days can be negative -> already expired
spdlog::info("IsCertExpiring: certificate expires in {} days and {} seconds", days, secs);
if (days < 0)
{
return true; // already expired
}
if (days == 0 && secs <= 0)
{
return true;
}
return days <= daysThreshold;
}
struct ParsedUrl
{
std::string scheme;
std::string host;
int port;
};
static ParsedUrl ParseBaseUrl(const std::string &base)
{
// same simple regex as in DeviceControlService
std::regex re(R"(^\s*(https?)://([^/:]+)(?::(\d+))?\s*$)");
std::smatch m;
if (!std::regex_match(base, m, re))
{
throw std::runtime_error("EnrollmentService: invalid base URL: " + base);
}
ParsedUrl u;
u.scheme = m[1].str();
u.host = m[2].str();
u.port = m[3].matched ? std::stoi(m[3].str()) : (u.scheme == "https" ? 443 : 80);
return u;
}
// create SSL client with: CA from /etc/iot/keys, device cert from disk,
// key from *kernel keyring* (decrypted to a temp file)
static std::unique_ptr<httplib::SSLClient> MakeMtlsClient(const ParsedUrl &u)
{
// 1) choose CA
std::filesystem::path ca = "/etc/iot/keys/issuing_ca.pem";
if (!std::filesystem::exists(ca))
{
ca = "/etc/iot/keys/ca_chain.pem";
}
// 2) device cert
const std::filesystem::path crt = "/etc/iot/keys/device.crt.pem";
if (!std::filesystem::exists(crt))
{
throw std::runtime_error("MakeMtlsClient: device.crt.pem not found");
}
// 3) read key payload from kernel keyring and dump to temp (like DeviceControlService)
auto payload = snoop::device_sec::ReadClientKeyPayloadFromKeyring();
snoop::device_sec::TempFile tf(std::filesystem::temp_directory_path());
tf.write_all(payload.data(), payload.size());
// 4) build SSL client
auto cli = std::make_unique<httplib::SSLClient>(u.host.c_str(),
u.port,
crt.string().c_str(),
tf.path.string().c_str(),
std::string() /*no password*/);
// NOTE: we keep verification relaxed for now, like in your task loop
cli->enable_server_certificate_verification(false);
cli->set_ca_cert_path(ca.string().c_str());
cli->set_connection_timeout(10);
cli->set_read_timeout(60);
cli->set_write_timeout(60);
if (!cli->is_valid())
{
throw std::runtime_error("MakeMtlsClient: SSLClient not valid");
}
return cli;
}
};
} // namespace snoop

451
src/Services/WhipClient.h Normal file
View File

@@ -0,0 +1,451 @@
// src/Services/WhipClient.h
#pragma once
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <memory>
#include <string>
#include <string_view>
#include <atomic>
#include <mutex>
#include <optional>
#include <filesystem>
#include <vector>
#include <regex>
#include <array>
#include <random>
#include <cstdint>
#include <cstring>
#include <sstream>
#include <spdlog/spdlog.h>
#include <nlohmann/json.hpp>
#include <httplib.h> // build with CPPHTTPLIB_OPENSSL_SUPPORT
#include <rtc/rtc.hpp>
#include <rtc/peerconnection.hpp>
#include <rtc/description.hpp>
#include <rtc/track.hpp>
#include "Security/TlsKeyUtil.h"
#include <locale>
namespace snoop
{
class WhipClient
{
public:
struct Params
{
std::string whipUrl; // full WHIP endpoint, may include ?token=...
std::string caPath; // CA chain
std::string crtPath; // client cert
int sampleRate = 48000;
int channels = 1; // RTP timestamp increments by PCM frames/channel
};
explicit WhipClient(Params p) : m_p(std::move(p)) {}
~WhipClient() { Stop(); }
void Start()
{
std::lock_guard lk(m_mtx);
if (m_started)
{
return;
}
// Parse & normalize the provided URL now (extract token, add trailing /whip)
m_endpoint = ParseWhipUrl(m_p.whipUrl);
spdlog::info("WHIP normalized: scheme='{}' host='{}' port={} path='{}' auth='{}'",
m_endpoint.scheme, m_endpoint.host, m_endpoint.port, m_endpoint.path,
m_endpoint.bearer ? "yes" : "no");
rtc::Configuration cfg;
m_pc = std::make_shared<rtc::PeerConnection>(cfg);
m_pc->onStateChange([this](rtc::PeerConnection::State s)
{ spdlog::info("WHIP pc state: {}", (int)s); });
m_pc->onGatheringStateChange([this](rtc::PeerConnection::GatheringState s)
{ spdlog::info("WHIP gathering state: {}", (int)s); });
m_pc->onLocalDescription([this](rtc::Description desc)
{
try {
const std::string offer = std::string(desc);
spdlog::info("Local SDP (first 8 lines):");
{
std::istringstream is(offer);
for (int i = 0; i < 8 && is; ++i) {
std::string line; std::getline(is, line);
spdlog::info(" {}", line);
}
}
auto [answer, resourceUrl] = PostOfferWHIP(offer);
m_resourceUrl = resourceUrl;
m_pc->setRemoteDescription(rtc::Description(answer, "answer"));
spdlog::info("WHIP remote description set");
} catch (const std::exception& e) {
spdlog::error("WHIP POST offer failed: {}", e.what());
} });
// m_pc->onLocalCandidate([this](rtc::Candidate c)
// {
// try { PatchCandidateWHIP(c); }
// catch (const std::exception& e) { spdlog::warn("WHIP PATCH candidate failed: {}", e.what()); } });
m_pc->onLocalCandidate([this](rtc::Candidate c)
{
try {
std::string frag = "a=" + std::string(c); // libdatachannel gives correct candidate line
PatchSdpFrag(frag); // sends with CRLF + right content-type
} catch (const std::exception& e) {
spdlog::warn("WHIP PATCH candidate failed: {}", e.what());
} });
// 2) When ICE gathering completes, send end-of-candidates
m_pc->onGatheringStateChange([this](rtc::PeerConnection::GatheringState s)
{
spdlog::info("WHIP gathering state: {}", (int)s);
if (s == rtc::PeerConnection::GatheringState::Complete)
{
PatchSdpFrag("a=end-of-candidates");
} });
// Create a sendonly audio track
rtc::Description::Audio audioDesc("audio", rtc::Description::Direction::SendOnly);
audioDesc.addOpusCodec(111);
m_track = m_pc->addTrack(audioDesc);
// Open/close signals
m_track->onOpen([this]
{
spdlog::info("WHIP track opened");
m_trackOpen = true; });
m_track->onClosed([this]
{
spdlog::info("WHIP track closed");
m_trackOpen = false; });
// RTP state
std::mt19937 rng{std::random_device{}()};
m_ssrc = std::uniform_int_distribution<uint32_t>()(rng);
m_seq = std::uniform_int_distribution<uint16_t>()(rng);
m_ts = 0; // 48k clock for Opus
// Create SDP offer (triggers onLocalDescription)
m_pc->setLocalDescription();
m_started = true;
}
void Stop()
{
std::lock_guard lk(m_mtx);
if (!m_started)
{
return;
}
m_trackOpen = false;
m_track.reset();
m_pc.reset();
m_resourceUrl.reset();
m_started = false;
}
// Push encoded Opus frames as RTP over the libdatachannel track.
void PushOpus(const unsigned char *opusData, size_t opusBytes, int pcmFramesPerChannel)
{
std::lock_guard lk(m_mtx);
if (!m_track || !m_started || !m_trackOpen.load())
{
return;
}
std::array<uint8_t, 12> rtp{};
rtp[0] = 0x80; // V=2
rtp[1] = 0x80 | (m_pt & 0x7F); // M=1, PT
rtp[2] = uint8_t(m_seq >> 8);
rtp[3] = uint8_t(m_seq & 0xFF);
rtp[4] = uint8_t(m_ts >> 24);
rtp[5] = uint8_t((m_ts >> 16) & 0xFF);
rtp[6] = uint8_t((m_ts >> 8) & 0xFF);
rtp[7] = uint8_t(m_ts & 0xFF);
rtp[8] = uint8_t(m_ssrc >> 24);
rtp[9] = uint8_t((m_ssrc >> 16) & 0xFF);
rtp[10] = uint8_t((m_ssrc >> 8) & 0xFF);
rtp[11] = uint8_t(m_ssrc & 0xFF);
rtc::binary packet;
packet.resize(rtp.size() + opusBytes);
std::memcpy(packet.data(), rtp.data(), rtp.size());
std::memcpy(packet.data() + rtp.size(), opusData, opusBytes);
m_track->send(packet);
++m_seq;
m_ts += static_cast<uint32_t>(pcmFramesPerChannel);
}
private:
struct ParsedUrl
{
std::string scheme;
std::string host;
int port = 0;
std::string path; // normalized; includes leading '/', **ends with '/whip'**
std::optional<std::string> bearer; // token from ?token=...
};
Params m_p;
std::shared_ptr<rtc::PeerConnection> m_pc;
std::shared_ptr<rtc::Track> m_track;
std::optional<std::string> m_resourceUrl;
std::mutex m_mtx;
std::atomic<bool> m_started{false};
std::atomic<bool> m_trackOpen{false};
ParsedUrl m_endpoint;
// RTP state
uint32_t m_ssrc = 0;
uint16_t m_seq = 0;
uint32_t m_ts = 0;
uint8_t m_pt = 111;
// ---------- URL parsing & normalization ----------
static ParsedUrl ParseWhipUrl(const std::string &input)
{
// scheme://host[:port]/path[?query]
static const std::regex re(R"(^(https?)://([^/:]+)(?::(\d+))?(/[^?]*)?(?:\?(.+))?$)");
std::smatch m;
if (!std::regex_match(input, m, re))
{
throw std::runtime_error("Invalid WHIP URL: " + input);
}
ParsedUrl out;
out.scheme = m[1].str();
if (out.scheme != "https")
{
throw std::runtime_error("Only HTTPS WHIP endpoints are supported");
}
out.host = m[2].str();
out.port = m[3].matched ? std::stoi(m[3].str()) : 443;
std::string rawPath = m[4].matched ? m[4].str() : "/";
std::string query = m[5].matched ? m[5].str() : "";
// Extract token=... from query (no decoding needed for your JWT)
if (!query.empty())
{
// naive split, fine for `token=...`
size_t pos = 0;
while (pos < query.size())
{
size_t amp = query.find('&', pos);
if (amp == std::string::npos)
{
amp = query.size();
}
auto kv = query.substr(pos, amp - pos);
auto eq = kv.find('=');
if (eq != std::string::npos)
{
auto key = kv.substr(0, eq);
auto val = kv.substr(eq + 1);
if (key == "token" && !val.empty())
{
out.bearer = val;
}
}
pos = amp + (amp < query.size() ? 1 : 0);
}
}
// MediaMTX wants: /whip/<original...>/whip
// Your incoming URLs already start with /whip/... — we just ensure they end with /whip.
if (rawPath.empty())
{
rawPath = "/";
}
if (rawPath.back() == '/')
{
rawPath.pop_back();
}
if (rawPath.rfind("/whip", std::string::npos) != rawPath.size() - 5)
{
rawPath += "/whip";
}
out.path = rawPath;
return out;
}
static std::tuple<std::string, std::string> ExtractAnswerAndLocation(const httplib::Result &r)
{
if (!r)
{
throw std::runtime_error("No HTTP result");
}
if (r->status != 201 && r->status != 200)
{
throw std::runtime_error("Unexpected WHIP status: " + std::to_string(r->status));
}
std::string answer = r->body;
std::string resourceUrl;
if (r->has_header("Location"))
{
resourceUrl = r->get_header_value("Location");
}
return {answer, resourceUrl};
}
std::pair<std::string, std::string> PostOfferWHIP(const std::string &sdpOffer)
{
auto cli = MakeClient(m_endpoint);
httplib::Headers hs{
{"Content-Type", "application/sdp"},
{"Accept", "application/sdp"}};
if (m_endpoint.bearer)
{
hs.emplace("Authorization", "Bearer " + *m_endpoint.bearer);
}
spdlog::info("WHIP POST url='{}://{}:{}' path='{}' offer-bytes={}",
m_endpoint.scheme, m_endpoint.host, m_endpoint.port, m_endpoint.path, sdpOffer.size());
auto res = cli->Post(m_endpoint.path.c_str(), hs, sdpOffer, "application/sdp");
if (!res)
{
throw std::runtime_error("No HTTP result (network?)");
}
const auto ctype = res->get_header_value("Content-Type");
const bool has_loc = res->has_header("Location");
spdlog::info("WHIP POST -> status={} len={} location='{}'",
res->status, res->body.size(),
res->has_header("Location") ? res->get_header_value("Location") : "<none>",
ctype);
if (res->status != 201 && res->status != 200)
{
// Surface server body for debugging (404s, auth errors, etc.)
spdlog::error("WHIP POST body:\n{}", res->body);
throw std::runtime_error("Unexpected WHIP status: " + std::to_string(res->status));
}
std::string answer = res->body;
std::string resourceUrl;
if (res->has_header("Location"))
{
resourceUrl = res->get_header_value("Location");
}
if (answer.find("a=ice-ufrag:") == std::string::npos)
{
spdlog::warn("Answer SDP has no top-level a=ice-ufrag; continuing. Body (first lines):");
std::istringstream is(answer);
for (int i = 0; i < 12 && is; ++i)
{
std::string line;
std::getline(is, line);
spdlog::warn(" {}", line);
}
}
return {answer, resourceUrl};
}
void PatchCandidateWHIP(const rtc::Candidate &cand)
{
// If MediaMTX returns an absolute Location for the resource, we parse it;
// otherwise we reuse the same host/port and use Location as path.
if (!m_resourceUrl)
{
return;
}
ParsedUrl target;
try
{
target = ParseWhipUrl(*m_resourceUrl);
}
catch (...)
{
// Not a full URL; treat resourceUrl as path on the same host:port, still send auth header.
target = m_endpoint;
target.path = *m_resourceUrl;
}
auto cli = MakeClient(target);
std::string frag = "a=" + std::string(cand);
httplib::Headers hs{
{"Content-Type", "application/trickle-ice-sdpfrag"}};
if (m_endpoint.bearer)
{
hs.emplace("Authorization", "Bearer " + *m_endpoint.bearer);
}
auto res = cli->Patch(target.path.c_str(), hs, frag, "application/trickle-ice-sdpfrag");
if (!res || (res->status < 200 || res->status >= 300))
{
spdlog::warn("WHIP PATCH {} -> {}", target.path, res ? res->status : -1);
}
}
std::unique_ptr<httplib::SSLClient> MakeClient(const ParsedUrl &u)
{
if (u.scheme == "https")
{
#ifndef CPPHTTPLIB_OPENSSL_SUPPORT
throw std::runtime_error("https URL but CPPHTTPLIB_OPENSSL_SUPPORT not enabled");
#else
// mTLS: write client key payload to a temp file so OpenSSL can read it
auto payload = snoop::device_sec::ReadClientKeyPayloadFromKeyring();
snoop::device_sec::TempFile tf(std::filesystem::temp_directory_path());
tf.write_all(payload.data(), payload.size());
auto cli = std::make_unique<httplib::SSLClient>(u.host.c_str(), u.port,
m_p.crtPath.c_str(), tf.path.c_str(),
std::string());
cli->enable_server_certificate_verification(false);
cli->set_ca_cert_path(m_p.caPath.c_str());
cli->set_connection_timeout(10);
cli->set_read_timeout(60);
cli->set_write_timeout(60);
return cli;
#endif
}
}
void PatchSdpFrag(const std::string &sdpfrag)
{
if (!m_resourceUrl)
{
return;
}
// Reuse the same host/port and just set path = Location
ParsedUrl target = m_endpoint;
target.path = *m_resourceUrl;
auto cli = MakeClient(target);
httplib::Headers hs{
{"Content-Type", "application/trickle-ice-sdpfrag"}};
if (m_endpoint.bearer)
{
hs.emplace("Authorization", "Bearer " + *m_endpoint.bearer);
}
// MUST include CRLF at end of body
std::string body = sdpfrag;
if (body.empty() || body.back() != '\n')
{
body += "\r\n";
}
auto res = cli->Patch(target.path.c_str(), hs, body, "application/trickle-ice-sdpfrag");
if (!res || !(res->status == 200 || res->status == 201 || res->status == 204))
{
spdlog::warn("WHIP PATCH {} -> {}", target.path, res ? res->status : -1);
}
}
};
} // namespace snoop

View File

@@ -8,6 +8,9 @@
#include "Services/AudioStreamService.h"
#include "Services/AudioWriterService.h"
#include "Services/ConfigService.h"
#include "Services/EnrollmentService.h"
#include "Services/DeviceControlService.h"
#include "Security/TlsKeyUtil.h"
#ifdef USE_ALSA_ADAPTER
#include "AudioAdapters/AlsaAudioAdapter.h"
@@ -17,42 +20,236 @@ using AudioAdapter = snoop::AlsaAudioAdapter;
using AudioAdapter = snoop::PortAudioAdapter;
#endif
// sudo apt-get install libasound2-dev
namespace snoop {
template<AudioEncoderConcept TAudioEncoder, AudioWriterConcept TAudioWriter>
void Main() {
try {
namespace snoop
{
template <AudioEncoderConcept TAudioEncoder, AudioWriterConcept TAudioWriter>
void Main()
{
try
{
int sampleRate = 48000;
int channels = 1;
int framesPerBuffer = 2880;
auto configService = std::make_shared<ConfigService>( "config.json" );
auto configService = std::make_shared<ConfigService>("config.json");
auto sioClient = std::make_shared<sio::client>();
sioClient->connect( configService->GetBaseUrl() );
auto writerService = std::make_shared<AudioWriterService>( configService, "records" );
AudioStreamService streamer( sioClient, configService->GetGuid() );
TAudioEncoder encoder( sampleRate, channels, framesPerBuffer, [&]( auto input, auto size ) -> int {
streamer.SendAudioData( input, size );
writerService->WriteAudioData( input, size, framesPerBuffer );
return paContinue;
} );
while( true ) {
sleep( 1000 );
// ---- FIRST-RUN ENROLLMENT ----
// EnrollmentService enroll(configService);
// const bool didEnroll = enroll.EnsureEnrolled();
// if (didEnroll)
// {
// spdlog::info("First-run enrollment completed.");
// }
auto enrollSvc = std::make_shared<EnrollmentService>(configService);
enrollSvc->EnsureEnrolled();
try
{
enrollSvc->RenewCertificate(false);
}
catch (const std::exception &e)
{
spdlog::warn("Auto-renew check failed: {}", e.what());
}
} catch( const std::exception& ex ) {
spdlog::error( "Exception: {}", ex.what() );
// Ensure client key is in kernel keyring for this session
try
{
bool loaded = snoop::device_sec::EnsureClientKeyInKernelKeyring(configService->GetGuid());
if (loaded)
{
spdlog::info("Main: client key loaded into kernel keyring for this session");
}
else
{
spdlog::info("Main: client key already present in kernel keyring");
}
}
catch (const std::exception &e)
{
spdlog::warn("Main: failed to ensure client key in keyring: {}", e.what());
}
auto writerService = std::make_shared<AudioWriterService>(configService, "records");
// WHIP-only streamer
AudioStreamService streamer(configService);
// Encoder: keep writing to local Ogg via writerService; (no Socket.IO send)
TAudioEncoder encoder(sampleRate, channels, framesPerBuffer, [&](auto input, auto size) -> int
{
writerService->WriteAudioData(input, size, framesPerBuffer);
streamer.OnOpus(reinterpret_cast<const unsigned char*>(input),static_cast<size_t>(size),framesPerBuffer);
return paContinue; });
snoop::DeviceControlService::Controls controls{
.stopStreamNow = [&]()
{ streamer.StopWhip(); },
.stopRecordingNow = [&]()
{ writerService->StopRecordingNow(); }};
snoop::DeviceControlService::Handlers handlers{};
static std::unique_ptr<snoop::DeviceControlService> g_taskSvc;
// ------------------------------
{
handlers.onStartStream = [&](const snoop::DeviceControlService::Task &t)
{
spdlog::info("start_stream payload: {}", t.payload.dump());
// TODO: start your streaming pipeline using payload["whipUrl"], etc.
std::string whipUrl;
try
{
if (t.payload.contains("whipUrl"))
{
whipUrl = t.payload.at("whipUrl").get<std::string>();
}
}
catch (...)
{
}
if (whipUrl.empty())
{
return snoop::DeviceControlService::HandlerResult{false, "{}", "whipUrl missing"};
}
bool ok = streamer.StartWhip(whipUrl, sampleRate, channels);
return snoop::DeviceControlService::HandlerResult{ok, R"({"whip":"started"})", ok ? "" : "failed"};
};
handlers.onStopStream = [&](const snoop::DeviceControlService::Task &t)
{
spdlog::info("stop_stream payload: {}", t.payload.dump());
// TODO: stop streaming
streamer.StopWhip();
return snoop::DeviceControlService::HandlerResult{true, R"({"status":"stopped"})", ""};
};
handlers.onStartRecording = [&](const snoop::DeviceControlService::Task &t)
{
spdlog::info("start_recording payload: {}", t.payload.dump());
// TODO: if you gate writes in AudioWriterService, flip to recording mode here
writerService->StartRecording();
return snoop::DeviceControlService::HandlerResult{true, R"({"recording":"on"})", ""};
};
handlers.onStopRecording = [&](const snoop::DeviceControlService::Task &t)
{
spdlog::info("stop_recording payload: {}", t.payload.dump());
// TODO: flip to recording off
writerService->StopRecordingGracefully();
return snoop::DeviceControlService::HandlerResult{true, R"({"recording":"off"})", ""};
};
handlers.onUpdateConfig = [&](const snoop::DeviceControlService::Task &t)
{
spdlog::info("update_config payload: {}", t.payload.dump());
try
{
if (t.payload.contains("duration"))
{
auto ms = static_cast<unsigned long long>(t.payload.at("duration").get<int>() * 1000ULL);
configService->SetRecordingDuration(ms);
}
if (t.payload.contains("sleep"))
{
configService->SetPollingInterwall(t.payload.at("sleep").get<int>());
}
if (t.payload.contains("jitter"))
{
configService->SetJitter(t.payload.at("jitter").get<int>());
}
if (t.payload.contains("endpoint"))
{
configService->SetBaseUrl(t.payload.at("endpoint").get<std::string>());
}
return snoop::DeviceControlService::HandlerResult{true, R"({"updated":true})", ""};
}
catch (const std::exception &e)
{
return snoop::DeviceControlService::HandlerResult{false, "{}", e.what()};
}
};
handlers.onSetDeepSleep = [](const snoop::DeviceControlService::Task &t)
{
spdlog::info("set_deep_sleep payload: {}", t.payload.dump());
// TODO: use platform power management or just extend sleep window
try
{
long long startMs = 0, stopMs = 0;
if (t.payload.contains("start_ms") && t.payload.contains("stop_ms"))
{
startMs = t.payload.at("start_ms").get<long long>();
stopMs = t.payload.at("stop_ms").get<long long>();
}
else if (t.payload.contains("start") && t.payload.contains("stop"))
{
auto s = snoop::DeviceControlService::ParseRfc3339UtcToMs(t.payload["start"].get<std::string>());
auto e = snoop::DeviceControlService::ParseRfc3339UtcToMs(t.payload["stop"].get<std::string>());
if (!s || !e)
{
throw std::runtime_error("Invalid RFC3339 times");
}
startMs = *s;
stopMs = *e;
}
else if (t.payload.contains("start") && t.payload["start"].is_string() && t.payload["start"].get<std::string>() == "now" && t.payload.contains("for_s"))
{
startMs = snoop::DeviceControlService::NowMs();
stopMs = startMs + t.payload["for_s"].get<long long>() * 1000LL;
}
else
{
throw std::runtime_error("Missing/invalid start/stop time");
}
if (stopMs <= startMs)
{
throw std::runtime_error("stop <= start");
}
g_taskSvc->ArmDeepSleep(startMs, stopMs);
return snoop::DeviceControlService::HandlerResult{
true,
std::string("{\"start_ms\":") + std::to_string(startMs) + ",\"stop_ms\":" + std::to_string(stopMs) + "}",
""};
}
catch (const std::exception &e)
{
return snoop::DeviceControlService::HandlerResult{false, "{}", e.what()};
}
};
handlers.onRenewCert = [enrollSvc](const snoop::DeviceControlService::Task &t)
{
spdlog::info("renew_cert task received, payload: {}", t.payload.dump());
try
{
bool ok = enrollSvc->RenewCertificate(true); // <- force
if (ok)
{
return snoop::DeviceControlService::HandlerResult{true, R"({"renewed":true})", ""};
}
return snoop::DeviceControlService::HandlerResult{true, R"({"renewed":false})", ""};
}
catch (const std::exception &e)
{
return snoop::DeviceControlService::HandlerResult{false, "{}", e.what()};
}
};
}
g_taskSvc = std::make_unique<snoop::DeviceControlService>(configService, handlers, controls);
while (true)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
catch (const std::exception &ex)
{
spdlog::error("Exception: {}", ex.what());
}
}
}
}
int main() {
int main()
{
snoop::Main<snoop::OpusEncoder<AudioAdapter>, snoop::OggAudioWriter>();
return 0;
}

1
third_party/libdatachannel vendored Submodule

Submodule third_party/libdatachannel added at d7719c3130

View File

@@ -1,26 +0,0 @@
name: CI
on:
push:
pull_request:
schedule:
- cron: '0 0 * * 0'
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v2.0.0
- name: Build project
uses: nicledomaS/cmake_build_action@v1.3
with:
submodule_update: ON
run_tests: ON
unit_test_build: -DBUILD_UNIT_TESTS=ON
cmake_args: -DCMAKE_CXX_FLAGS=-std=c++11

View File

@@ -1,12 +0,0 @@
build/
**/*.user
CMakeCache.txt
CMakeFiles/
Makefile
cmake_install.cmake
install_manifest.txt
libsioclient.a
sio_test
.DS_Store
.cache/

View File

@@ -1,9 +0,0 @@
[submodule "lib/websocketpp"]
path = lib/websocketpp
url = https://github.com/zaphoyd/websocketpp.git
[submodule "lib/rapidjson"]
path = lib/rapidjson
url = https://github.com/miloyip/rapidjson.git
[submodule "lib/asio"]
path = lib/asio
url = https://github.com/chriskohlhoff/asio.git

View File

@@ -1,209 +0,0 @@
## API
### *Overview*
There're just 3 roles in this library - `socket`, `client` and `message`.
`client` is for physical connection while `socket` is for "namespace" (which is like a logical channel), which means one `socket` paired with one namespace, and one `client` paired with one physical connection.
Since a physical connection can have multiple namespaces (which is called multiplex), a `client` object may have multiple `socket` objects, each of which is bound to a distinct `namespace`.
Use `client` to setup the connection to the server, manange the connection status, also session id for the connection.
Use `socket` to send messages under namespace and receives messages in the namespace, also handle special types of message.
The `message` is just about the content you want to send, with text, binary or structured combinations.
### *Socket*
#### Constructors
Sockets are all managed by `client`, no public constructors.
You can get it's pointer by `client.socket(namespace)`.
#### Event Emitter
`void emit(std::string const& name, message::list const& msglist, std::function<void (message::ptr const&)> const& ack)`
Universal event emission interface, by applying implicit conversion magic, it is backward compatible with all previous `emit` interfaces.
#### Event Bindings
`void on(std::string const& event_name,event_listener const& func)`
`void on(std::string const& event_name,event_listener_aux const& func)`
Bind a callback to specified event name. Same as `socket.on()` function in JS, `event_listener` is for full content event object, `event_listener_aux` is for convenience.
`void off(std::string const& event_name)`
Unbind the event callback with specified name.
`void off_all()`
Clear all event bindings (not including the error listener).
`void on_error(error_listener const& l)`
Bind the error handler for socket.io error messages.
`void off_error()`
Unbind the error handler.
```C++
//event object:
class event
{
public:
const std::string& get_nsp() const;
const std::string& get_name() const;
const message::ptr& get_message() const;
bool need_ack() const;
void put_ack_message(message::ptr const& ack_message);
message::ptr const& get_ack_message() const;
...
};
//event listener declare:
typedef std::function<void(const std::string& name,message::ptr const& message,bool need_ack, message::ptr& ack_message)> event_listener_aux;
typedef std::function<void(event& event)> event_listener;
typedef std::function<void(message::ptr const& message)> error_listener;
```
#### Connect and close socket
`connect` will happen for existing `socket`s automatically when `client` have opened up the physical connection.
`socket` opened with connected `client` will connect to its namespace immediately.
`void close()`
Positively disconnect from namespace.
#### Get name of namespace
`std::string const& get_namespace() const`
Get current namespace name which the client is inside.
### *Client*
#### Constructors
`client()` default constructor.
#### Connection Listeners
`void set_open_listener(con_listener const& l)`
Call when websocket is open, especially means good connectivity.
`void set_fail_listener(con_listener const& l)`
Call when failed in connecting.
`void set_close_listener(close_listener const& l)`
Call when closed or drop. See `client::close_reason`
```C++
//connection listener declare:
enum close_reason
{
close_reason_normal,
close_reason_drop
};
typedef std::function<void(void)> con_listener;
typedef std::function<void(close_reason const& reason)> close_listener;
```
#### Socket listeners
`void set_socket_open_listener(socket_listener const& l)`
Set listener for socket connect event, called when any sockets being ready to send message.
`void set_socket_close_listener(socket_listener const& l)`
Set listener for socket close event, called when any sockets being closed, afterward, corresponding `socket` object will be cleared from client.
```C++
//socket_listener declare:
typedef std::function<void(std::string const& nsp)> socket_listener;
```
#### Connect and Close
`void connect(const std::string& uri)`
Connect to socket.io server, e.g., `client.connect("ws://localhost:3000");`
`void close()`
Close the client, return immediately.
`void sync_close()`
Close the client, don't return until it is really closed.
`bool opened() const`
Check if client's connection is opened.
#### Transparent reconnecting
`void set_reconnect_attempts(int attempts)`
Set max reconnect attempts, set to 0 to disable transparent reconnecting.
`void set_reconnect_delay(unsigned millis)`
Set minimum delay for reconnecting, this is the delay for 1st reconnecting attempt,
then the delay duration grows by attempts made.
`void set_reconnect_delay_max(unsigned millis)`
Set maximum delay for reconnecting.
`void set_reconnecting_listener(con_listener const& l)`
Set listener for reconnecting is in process.
`void set_reconnect_listener(reconnect_listener const& l)`
Set listener for reconnecting event, called once a delayed connecting is scheduled.
#### Logs
`void set_logs_default()`
Configure logs to the default level (connect, disconnect, app)
`void set_logs_quiet()`
Configure logs to the quiet level
`void set_logs_verbose()`
Configure logs to the verbose level
#### Namespace
`socket::ptr socket(std::string const& nsp)`
Get a pointer to a socket which is paired with the specified namespace.
#### Session ID
`std::string const& get_sessionid() const`
Get socket.io session id.
### *Message*
`message` Base class of all message object.
`int_message` message contains a 64-bit integer.
`double_message` message contains a double.
`string_message` message contains a string.
`array_message` message contains a `vector<message::ptr>`.
`object_message` message contains a `map<string,message::ptr>`.
`message::ptr` pointer to `message` object, it will be one of its derived classes, judge by `message.get_flag()`.
All designated constructor of `message` objects is hidden, you need to create message and get the `message::ptr` by `[derived]_message:create()`.

View File

@@ -1,40 +0,0 @@
# [2.1.0](https://github.com/socketio/socket.io-client-cpp/compare/2.0.0...2.1.0) (2021-10-12)
### Bug Fixes
* fix ASIO_STANDALONE release build trying to use boost::random ([#301](https://github.com/socketio/socket.io-client-cpp/issues/301)) ([168ce9d](https://github.com/socketio/socket.io-client-cpp/commit/168ce9d10b4ac667c43fe16b4cf530f6a3749235))
* fix LOG call syntax ([#301](https://github.com/socketio/socket.io-client-cpp/issues/301)) ([c09221f](https://github.com/socketio/socket.io-client-cpp/commit/c09221f357effe1a5a0fc0e7d7902eba1ab0484d))
### Features
* support TLSv1.2 and newer ([#321](https://github.com/socketio/socket.io-client-cpp/issues/321)) ([7c60ba9](https://github.com/socketio/socket.io-client-cpp/commit/7c60ba9d1e5e58de57f127025bcf69f4baecd2b4))
# [3.1.0](https://github.com/socketio/socket.io-client-cpp/compare/3.0.0...3.1.0) (2021-10-12)
### Bug Fixes
* lower the minimum CMake supported version ([b196fa7](https://github.com/socketio/socket.io-client-cpp/commit/b196fa7537cd3f7bed626ead873a7b71d1293c0d))
* handle closing sockets upon on_fail events ([d1c73b7](https://github.com/socketio/socket.io-client-cpp/commit/d1c73b73a8f536da3d353eac2a560af9791b13e3))
* resolve client_impl::ping LOG call syntax in debug builds ([e7de4eb](https://github.com/socketio/socket.io-client-cpp/commit/e7de4ebf64f4f49e18594a2c093c07beb963579a))
### Features
* allow resource path to be set in connection URI ([#134](https://github.com/socketio/socket.io-client-cpp/issues/134)) ([36a8cd4](https://github.com/socketio/socket.io-client-cpp/commit/36a8cd45272aa51f0f6ef27aa4744dbc6e8421f7))
* add support for logging configuration ([1b42ce7](https://github.com/socketio/socket.io-client-cpp/commit/1b42ce738f4c3e260f79bcb143bfe6efcdce5709))
* support TLSv1.2 and newer ([#321](https://github.com/socketio/socket.io-client-cpp/issues/321)) ([82d39a9](https://github.com/socketio/socket.io-client-cpp/commit/82d39a90ef118500a0329d214eec331db983bd74))
# [3.0.0](https://github.com/socketio/socket.io-client-cpp/compare/2.0.0...3.0.0) (2021-01-09)
### Features
* add support for Socket.IO v3 ([ec4d540](https://github.com/socketio/socket.io-client-cpp/commit/ec4d540ad54593604ac2091e67ffc2a6d9a00db6))

View File

@@ -1,177 +0,0 @@
cmake_minimum_required(VERSION 3.12...3.27)
PROJECT(sioclient
VERSION 3.1.0
)
option(BUILD_SHARED_LIBS "Build the shared library" OFF)
option(BUILD_UNIT_TESTS "Builds unit tests target" OFF)
option(USE_SUBMODULES "Use source in local submodules instead of system libraries" ON)
option(DISABLE_LOGGING "Do not print logging messages" OFF)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(DEFAULT_BUILD_TYPE "Release")
message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
# Only do these if this is the main project, and not if it is included through add_subdirectory
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
# Testing only available if this is the main app
# Note this needs to be done in the main CMakeLists
# since it calls enable_testing, which must be in the
# main CMakeLists.
include(CTest)
endif()
add_definitions(
# These will force ASIO to compile without Boost
-DBOOST_DATE_TIME_NO_LIB
-DBOOST_REGEX_NO_LIB
-DASIO_STANDALONE
# These will force sioclient to compile with C++11
-D_WEBSOCKETPP_CPP11_STL_
-D_WEBSOCKETPP_CPP11_FUNCTIONAL_
-D_WEBSOCKETPP_CPP11_TYPE_TRAITS_
-D_WEBSOCKETPP_CPP11_CHRONO_
)
if (DISABLE_LOGGING)
add_definitions(-DSIO_DISABLE_LOGGING)
endif()
set(ALL_SRC
"src/sio_client.cpp"
"src/sio_socket.cpp"
"src/internal/sio_client_impl.cpp"
"src/internal/sio_packet.cpp"
)
add_library(sioclient ${ALL_SRC})
if(USE_SUBMODULES)
set(MODULE_INCLUDE_DIRS
${CMAKE_CURRENT_LIST_DIR}/lib/websocketpp
${CMAKE_CURRENT_LIST_DIR}/lib/rapidjson/include
${CMAKE_CURRENT_LIST_DIR}/lib/asio/asio/include
)
else()
find_package(websocketpp CONFIG REQUIRED)
find_package(asio CONFIG REQUIRED)
find_package(RapidJSON CONFIG REQUIRED)
target_link_libraries(sioclient PRIVATE websocketpp::websocketpp asio::asio rapidjson)
endif()
include(GNUInstallDirs)
target_include_directories(sioclient
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE
${MODULE_INCLUDE_DIRS}
)
target_compile_features(sioclient PUBLIC cxx_std_11)
find_package(Threads REQUIRED)
target_link_libraries(sioclient PUBLIC Threads::Threads)
if(BUILD_SHARED_LIBS)
set_target_properties(sioclient
PROPERTIES
SOVERSION ${PROJECT_VERSION_MAJOR}
VERSION ${PROJECT_VERSION}
)
endif()
list(APPEND TARGET_LIBRARIES sioclient)
find_package(OpenSSL)
if(OPENSSL_FOUND)
add_library(sioclient_tls ${ALL_SRC})
target_include_directories(sioclient_tls PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE
${MODULE_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}
)
target_compile_features(sioclient_tls PUBLIC cxx_std_11)
target_link_libraries(sioclient_tls PRIVATE OpenSSL::SSL OpenSSL::Crypto)
if (NOT USE_SUBMODULES)
target_link_libraries(sioclient_tls PRIVATE websocketpp::websocketpp asio asio::asio rapidjson)
endif()
target_compile_definitions(sioclient_tls PRIVATE -DSIO_TLS)
target_link_libraries(sioclient_tls PUBLIC Threads::Threads)
if(BUILD_SHARED_LIBS)
set_target_properties(sioclient_tls
PROPERTIES
SOVERSION ${PROJECT_VERSION_MAJOR}
VERSION ${PROJECT_VERSION}
)
endif()
list(APPEND TARGET_LIBRARIES sioclient_tls)
endif()
export(PACKAGE sioclient)
file(GLOB ALL_HEADERS ${CMAKE_CURRENT_LIST_DIR}/src/*.h)
install(FILES ${ALL_HEADERS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(TARGETS ${TARGET_LIBRARIES} EXPORT sioclientTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
# === generate a CMake Config File ===
include(CMakePackageConfigHelpers)
set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/sioclient)
string(REGEX REPLACE "([^;]+)" "find_dependency(\\1)" _find_dependency_calls "${_package_dependencies}")
string(REPLACE ";" "\n" _find_dependency_calls "${_find_dependency_calls}")
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfigVersion.cmake"
VERSION ${sioclient_VERSION}
COMPATIBILITY AnyNewerVersion
)
export(EXPORT sioclientTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientTargets.cmake"
NAMESPACE sioclient::
)
configure_package_config_file(sioclientConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfig.cmake"
INSTALL_DESTINATION "${ConfigPackageLocation}"
)
install(EXPORT sioclientTargets
NAMESPACE
sioclient::
DESTINATION
${ConfigPackageLocation}
)
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/sioclient/sioclientTargets.cmake"
DESTINATION
${ConfigPackageLocation}
)
if((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) OR BUILD_UNIT_TESTS)
add_subdirectory(test)
endif()

View File

@@ -1,39 +0,0 @@
## Install
### With CMake
1. Use `git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git` to clone your local repo.
2. Run `cmake ./`
3. Run `make install`(if makefile generated) or open generated project (if project file generated) to build.
4. Outputs is under `./build`, link with the all static libs under `./build/lib` and include headers under `./build/include` in your client code where you want to use it.
### Without CMake
1. Use `git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git` to clone your local repo.
2. Add `./lib/asio/asio/include`, `./lib/websocketpp` and `./lib/rapidjson/include` to headers search path.
3. Include all files under `./src` in your project, add `sio_client.cpp`,`sio_socket.cpp`,`internal/sio_client_impl.cpp`, `internal/sio_packet.cpp` to source list.
4. Add `BOOST_DATE_TIME_NO_LIB`, `BOOST_REGEX_NO_LIB`, `ASIO_STANDALONE`, `_WEBSOCKETPP_CPP11_STL_` and `_WEBSOCKETPP_CPP11_FUNCTIONAL_` to the preprocessor definitions
5. Include `sio_client.h` in your client code where you want to use it.
### With vcpkg
You can download and install the Socket.IO C++ client using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
```bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install socket-io-client
```
The Socket.IO client port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
### With Conan
You can install pre-built binaries for Socket.IO C++ client or build it from source using [Conan](https://conan.io/). Use the following command:
```
conan install --requires="sioclient/[*]" --build=missing
```
The Socket.IO client Conan recipe is kept up to date by Conan maintainers and community contributors.
If the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the ConanCenterIndex repository.

View File

@@ -1,27 +0,0 @@
## iOS
### Option 1: Create a static library
1. Create a static library
1. Copy the header files into xcode
Use the static libraries generated by the example project [iOS example project](examples/iOS)
Create one for
- release iphoneos
- release simulator
- debug iphoneos
- debug simulator
Join the debug libraries and the release libraries with e.g.
```
libtool -static -o libUniversalRelease.a Release-iphoneos/libsioclient.a Release-iphonesimulator/libsioclient.a
libtool -static -o libUniversalDebug.a Debug-iphoneos/libsioclient.a Debug-iphonesimulator/libsioclient.a
```
### Option 2: Manual integration
Use this [shell](https://gist.github.com/melode11/a90114a2abf009ca22ea) to download and build boost completely automattically. It installs boost to `<shell folder>/prefix`.
See the [iOS example project](examples/iOS) for how to integrate the rest.

View File

@@ -1,20 +0,0 @@
Copyright (c) 2015, Melo Yao
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to all conditions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,131 +0,0 @@
# Socket.IO C++ Client
[![Build Status](https://github.com/socketio/socket.io-client-cpp/workflows/CI/badge.svg)](https://github.com/socketio/socket.io-client-cpp/actions)
By virtue of being written in C++, this client works in several different platforms. The [examples](https://github.com/socketio/socket.io-client-cpp/tree/master/examples) folder contains an iPhone, QT and Console example chat client! It depends on [websocket++](https://github.com/zaphoyd/websocketpp) and is inspired by [socket.io-clientpp](https://github.com/ebshimizu/socket.io-clientpp).
[![Clients with iPhone, QT, Console and web](https://cldup.com/ukvVVZmvYV.png)](https://github.com/socketio/socket.io-client-cpp/tree/master/examples)
## Compatibility table
<table>
<tr>
<th rowspan="2">C++ Client version</th>
<th colspan="2">Socket.IO server version</th>
</tr>
<tr>
<td align="center">1.x / 2.x</td>
<td align="center">3.x / 4.x</td>
</tr>
<tr>
<td>2.x (<code>2.x</code> branch)</td>
<td align="center">YES</td>
<td align="center">YES, with <code><a href="https://socket.io/docs/v4/server-initialization/#allowEIO3">allowEIO3: true</a></code></td>
</tr>
<tr>
<td>3.x (<code>master</code> branch)</td>
<td align="center">NO</td>
<td align="center">YES</td>
</tr>
</table>
## Features
- 100% written in modern C++11
- Binary support
- Automatic JSON encoding
- Multiplex support
- Similar API to the Socket.IO JS client
- Cross platform
Note: Only the WebSocket transport is currently implemented (no fallback to HTTP long-polling)
## Installation alternatives
* [With CMAKE](./INSTALL.md#with-cmake)
* [Without CMAKE](./INSTALL.md#without-cmake)
* [With VCPKG](./INSTALL.md#with-vcpkg)
* [With Conan](./INSTALL.md#with-conan)
* [iOS and OS X](./INSTALL_IOS.md)
* Option 1: Cocoapods
* Option 2: Create a static library
* Option 3: Manual integration
## Quickstart
** [Full overview of API can be seen here](./API.md) **
The APIs are similar to the JS client.
#### Connect to a server
```C++
sio::client h;
h.connect("http://127.0.0.1:3000");
```
#### Emit an event
```C++
// emit event name only:
h.socket()->emit("login");
// emit text
h.socket()->emit("add user", username);
// emit binary
char buf[100];
h.socket()->emit("add user", std::make_shared<std::string>(buf,100));
// emit message object with lambda ack handler
h.socket()->emit("add user", string_message::create(username), [&](message::list const& msg) {
});
// emit multiple arguments
message::list li("sports");
li.push(string_message::create("economics"));
socket->emit("categories", li);
```
Items in `message::list` will be expanded in server side event callback function as function arguments.
#### Bind an event
##### Bind with function pointer
```C++
void OnMessage(sio::event &)
{
}
h.socket()->on("new message", &OnMessage);
```
##### Bind with lambda
```C++
h.socket()->on("login", [&](sio::event& ev)
{
//handle login message
//post to UI thread if any UI updating.
});
```
##### Bind with member function
```C++
class MessageHandler
{
public:
void OnMessage(sio::event &);
};
MessageHandler mh;
h.socket()->on("new message",std::bind( &MessageHandler::OnMessage,&mh,std::placeholders::_1));
```
#### Using namespace
```C++
h.socket("/chat")->emit("add user", username);
```
** [Full overview of API can be seen here](./API.md) **
## License
MIT

View File

@@ -1,9 +0,0 @@
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
find_package(Threads REQUIRED)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt)
add_executable(sio_console_demo main.cpp)
target_link_libraries(sio_console_demo sioclient)
target_link_libraries(sio_console_demo Threads::Threads)
target_compile_features(sio_console_demo PRIVATE cxx_std_11)
message(STATUS ${Boost_INCLUDE_DIRS} )
#target_include_directories(sio_console_demo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src" ${Boost_INCLUDE_DIRS} )

View File

@@ -1,28 +0,0 @@
#SioChatDemo setup
This Demo is create with `Visual Studio 2012 Update 4` , It is a simple console app, connect to official [socket.io chatroom example](https://github.com/Automattic/socket.io/tree/master/examples/chat) as a chat client.
You can choose a nickname, send/receive message in the chat room, and see join/left information as well.
##boost for windows
Please download boost package from [boost.org](www.boost.org), and unpack to `boost` folder.
Please make sure there's no redundent folder levels under it (by check if `bootstrap.bat` is directly under `boost` folder).
cd to `boost` folder, and run `bootstrap.bat`
Then run:
```shell
bjam stage --toolset=msvc --with-system --with-date_time --with-random --stagedir="release" link=static runtime-link=shared threading=multi release
bjam stage --toolset=msvc --with-system --with-date_time --with-random --stagedir="debug" link=static runtime-link=shared threading=multi debug
```
After done this, use Visual studio command line tool, go to `boost\release` folder, run
```shell
lib.exe /OUT:boost.lib *
```
And do then same thing in `boost\debug` folder.
then you can open the VS project `SioChatDemo.sln` to build and run.
##Visual studio version
Microsoft start to support c++11 after `Visual studio 2012 Update 4`. Please make sure you're using up-to-date version.

View File

@@ -1,20 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SioChatDemo", "SioChatDemo.vcxproj", "{3503FCEB-2C8E-441A-A57C-B9DEE9171CF4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3503FCEB-2C8E-441A-A57C-B9DEE9171CF4}.Debug|Win32.ActiveCfg = Debug|Win32
{3503FCEB-2C8E-441A-A57C-B9DEE9171CF4}.Debug|Win32.Build.0 = Debug|Win32
{3503FCEB-2C8E-441A-A57C-B9DEE9171CF4}.Release|Win32.ActiveCfg = Release|Win32
{3503FCEB-2C8E-441A-A57C-B9DEE9171CF4}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -1,101 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{3503FCEB-2C8E-441A-A57C-B9DEE9171CF4}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>SioChatDemo</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v110</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v110</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>D:\BoostRoot\include\boost-1_55;$(SolutionDir)..\..\..\lib\websocketpp;$(SolutionDir)..\..\..\lib\rapidjson\include;$(IncludePath)</IncludePath>
<LibraryPath>D:\boost_1_55_0\boost_build\debug\lib;$(SolutionDir)boost\$(Configuration)\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)boost;$(SolutionDir)..\..\..\lib\websocketpp;$(SolutionDir)..\..\..\lib\rapidjson\include;$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)boost\$(Configuration)\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>boost.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>boost.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\internal\sio_client_impl.h" />
<ClInclude Include="..\..\..\src\internal\sio_packet.h" />
<ClInclude Include="..\..\..\src\sio_client.h" />
<ClInclude Include="..\..\..\src\sio_message.h" />
<ClInclude Include="..\..\..\src\sio_socket.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\internal\sio_client_impl.cpp" />
<ClCompile Include="..\..\..\src\internal\sio_packet.cpp" />
<ClCompile Include="..\..\..\src\sio_client.cpp" />
<ClCompile Include="..\..\..\src\sio_socket.cpp" />
<ClCompile Include="..\main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -1,188 +0,0 @@
//
// sio_test_sample.cpp
//
// Created by Melo Yao on 3/24/15.
//
#include "../../src/sio_client.h"
#include <functional>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <string>
#ifdef WIN32
#define HIGHLIGHT(__O__) std::cout<<__O__<<std::endl
#define EM(__O__) std::cout<<__O__<<std::endl
#include <stdio.h>
#include <tchar.h>
#define MAIN_FUNC int _tmain(int argc, _TCHAR* argv[])
#else
#define HIGHLIGHT(__O__) std::cout<<"\e[1;31m"<<__O__<<"\e[0m"<<std::endl
#define EM(__O__) std::cout<<"\e[1;30;1m"<<__O__<<"\e[0m"<<std::endl
#define MAIN_FUNC int main(int argc ,const char* args[])
#endif
using namespace sio;
using namespace std;
std::mutex _lock;
std::condition_variable_any _cond;
bool connect_finish = false;
class connection_listener
{
sio::client &handler;
public:
connection_listener(sio::client& h):
handler(h)
{
}
void on_connected()
{
_lock.lock();
_cond.notify_all();
connect_finish = true;
_lock.unlock();
}
void on_close(client::close_reason const& reason)
{
std::cout<<"sio closed "<<std::endl;
exit(0);
}
void on_fail()
{
std::cout<<"sio failed "<<std::endl;
exit(0);
}
};
int participants = -1;
socket::ptr current_socket;
void bind_events()
{
current_socket->on("new message", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp)
{
_lock.lock();
string user = data->get_map()["username"]->get_string();
string message = data->get_map()["message"]->get_string();
EM(user<<":"<<message);
_lock.unlock();
}));
current_socket->on("user joined",sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp)
{
_lock.lock();
string user = data->get_map()["username"]->get_string();
participants = data->get_map()["numUsers"]->get_int();
bool plural = participants !=1;
// abc "
HIGHLIGHT(user<<" joined"<<"\nthere"<<(plural?" are ":"'s ")<< participants<<(plural?" participants":" participant"));
_lock.unlock();
}));
current_socket->on("user left", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp)
{
_lock.lock();
string user = data->get_map()["username"]->get_string();
participants = data->get_map()["numUsers"]->get_int();
bool plural = participants !=1;
HIGHLIGHT(user<<" left"<<"\nthere"<<(plural?" are ":"'s ")<< participants<<(plural?" participants":" participant"));
_lock.unlock();
}));
}
MAIN_FUNC
{
sio::client h;
connection_listener l(h);
h.set_open_listener(std::bind(&connection_listener::on_connected, &l));
h.set_close_listener(std::bind(&connection_listener::on_close, &l,std::placeholders::_1));
h.set_fail_listener(std::bind(&connection_listener::on_fail, &l));
h.connect("http://127.0.0.1:3000");
_lock.lock();
if(!connect_finish)
{
_cond.wait(_lock);
}
_lock.unlock();
current_socket = h.socket();
Login:
string nickname;
while (nickname.length() == 0) {
HIGHLIGHT("Type your nickname:");
getline(cin, nickname);
}
current_socket->on("login", sio::socket::event_listener_aux([&](string const& name, message::ptr const& data, bool isAck,message::list &ack_resp){
_lock.lock();
participants = data->get_map()["numUsers"]->get_int();
bool plural = participants !=1;
HIGHLIGHT("Welcome to Socket.IO Chat-\nthere"<<(plural?" are ":"'s ")<< participants<<(plural?" participants":" participant"));
_cond.notify_all();
_lock.unlock();
current_socket->off("login");
}));
current_socket->emit("add user", nickname);
_lock.lock();
if (participants<0) {
_cond.wait(_lock);
}
_lock.unlock();
bind_events();
HIGHLIGHT("Start to chat,commands:\n'$exit' : exit chat\n'$nsp <namespace>' : change namespace");
for (std::string line; std::getline(std::cin, line);) {
if(line.length()>0)
{
if(line == "$exit")
{
break;
}
else if(line.length() > 5&&line.substr(0,5) == "$nsp ")
{
string new_nsp = line.substr(5);
if(new_nsp == current_socket->get_namespace())
{
continue;
}
current_socket->off_all();
current_socket->off_error();
//per socket.io, default nsp should never been closed.
if(current_socket->get_namespace() != "/")
{
current_socket->close();
}
current_socket = h.socket(new_nsp);
bind_events();
//if change to default nsp, we do not need to login again (since it is not closed).
if(current_socket->get_namespace() == "/")
{
continue;
}
goto Login;
}
current_socket->emit("new message", line);
_lock.lock();
EM("\t\t\t"<<line<<":"<<"You");
_lock.unlock();
}
}
HIGHLIGHT("Closing...");
h.sync_close();
h.clear_con_listeners();
return 0;
}

View File

@@ -1 +0,0 @@
build-*

View File

@@ -1,290 +0,0 @@
![Demo shot](https://cldup.com/98tHyoJJE7.gif)
In this tutorial well learn how to create a QT chat application that communicates with a [Socket.IO Node.JS chat server](https://github.com/Automattic/socket.io/tree/master/examples/chat).
### Introduction
To follow along, start by cloning the repository: [socket.io-client-cpp](https://github.com/socketio/socket.io-client-cpp).
Using:
```bash
git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git
```
The app has the following features:
* Sending a message to all users joining to the room.
* Notifies when each user joins or leaves.
* Notifies when an user start typing a message.
###Install QT community
Visit [QT community download link](http://www.qt.io/download-open-source/#section-2) to get the install package.
Just install it with default installation option.
###Create a QT GUI application.
Launch QT Creator.
In welcome page, select `New Project`, create a `QT Widget Application`, named it `SioChatDemo`
The project structure is like:
```
SioChatDemo
|__ SioChatDemo.pro
|__Headers
| |__mainwindow.h
|__Sources
| |__main.cpp
| |__mainwindow.cpp
|__Forms
|__mainwindow.ui
```
### Import SioClient and config compile options.
Let's copy the SioClient into the QT project as a subfolder `sioclient`.
Edit `SioChatDemo.pro` to config paths and compile options, simply add:
```bash
SOURCES += ./sioclient/src/sio_client.cpp \
./sioclient/src/sio_packet.cpp
HEADERS += ./sioclient/src/sio_client.h \
./sioclient/src/sio_message.h
INCLUDEPATH += $$PWD/sioclient/lib/rapidjson/include
INCLUDEPATH += $$PWD/sioclient/lib/websocketpp
```
Also add two additional compile option
```bash
CONFIG+=no_keywords
CONFIG+=c++11
```
`no_keywords` is for preventing qmake treat some function's name `emit` as the keyword of signal-slot mechanism.
`c++11` ask for C++11 support.
### Make up mainwindow ui.
Make up a simple ui by drag and drop widget from `Widget box` in left side.
We finally end up with this:
![QT mainwindow](https://cldup.com/RI98CYpYL5.png)
It contains:
* a `QLineEdit` at the top for nickname inputing, named `nickNameEdit`
* a `QPushButton` at the topright for login, named `loginBtn`
* a `QListWidget` at the center for showing messages, named `listView`
* a `QLineEdit` at the bottom for typing message, named `messageEdit`
* a `QPushButton` at the bottomright for sending message, named `sendBtn`
### Add Slots in mainwindow
Slots need to be added in `mainwindow` class to handle UI events.They are
* click login button
* click send message button
* text change in messageEdit(for typing status)
* message editing is returned (for sending message by return)
Insert following code into `MainWindow` class in `mainwindow.h`
```C++
public Q_SLOTS:
void SendBtnClicked();
void TypingChanged();
void LoginClicked();
void OnMessageReturn();
```
### Connect UI event signal and slots together
Open `mainwindow.ui` in Design mode. switch to `signals/slots` mode by check `Menu->Edit->Edit Signals/Slots`
By press left mouse on widget and drag on to the window (cursor will become a sign of electrical ground), to open the connection editor.
In the connection editor, edit the slots of MainWindow at the right side, add Those slots function name added in `mainwindow.h` before.
Then we'll be able to connect the event signal from widget with our own slots.
We finally end up with this:
![QT signals&slots](https://cldup.com/Vsb-UXG3FC.jpg)
### Adding UI refresh Signals/Slots
`sio::client`'s callbacks are not in UI thread. However, UI is required to be updated by those callbacks, so we need some `Signal` for non-UI thread to "request" `Slots` functions been called in UI thread. Say if we want to signal `QListWidgetItem` being added, add:
```C++
//In mainwindow.h
Q_SIGNALS:
void RequestAddListItem(QListWidgetItem *item);
private Q_SLOTS:
void AddListItem(QListWidgetItem *item);
```
```C++
//In mainwindow.cpp
void MainWindow::AddListItem(QListWidgetItem* item)
{
this->findChild<QListWidget*>("listView")->addItem(item);
}
```
Then connect them in `MainWindow` constructor.
```C++
connect(this,SIGNAL(RequestAddListItem(QListWidgetItem*)),this,SLOT(AddListItem(QListWidgetItem*)));
```
### Init sio::client in MainWindow
For single window applications, simply let `MainWindow` class holding the `sio::client` object:
declare a `unique_ptr` member of `sio::client` and Several event handling functions in `mainwindow.h`
```C++
private:
void OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnConnected();
void OnClosed(client::close_reason const& reason);
void OnFailed();
std::unique_ptr<client> _io;
```
Init `sio::client` and setup event bindings for default `socket` in `MainWindow` constructor.
And we also need to handle the connectivity events, handle the connect and disconnect events.
Now the `MainWindow` constructor:
```C++
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
_io(new client())
{
ui->setupUi(this);
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
socket::ptr sock = _io->socket();
sock->on("new message",std::bind(&MainWindow::OnNewMessage,this,_1,_2,_3,_4));
sock->on("user joined",std::bind(&MainWindow::OnUserJoined,this,_1,_2,_3,_4));
sock->on("user left",std::bind(&MainWindow::OnUserLeft,this,_1,_2,_3,_4));
sock->on("typing",std::bind(&MainWindow::OnTyping,this,_1,_2,_3,_4));
sock->on("stop typing",std::bind(&MainWindow::OnStopTyping,this,_1,_2,_3,_4));
sock->on("login",std::bind(&MainWindow::OnLogin,this,_1,_2,_3,_4));
//default socket opened, also we have "set_open_listener" for monitoring physical connection opened.
_io->set_socket_open_listener(std::bind(&MainWindow::OnConnected,this,std::placeholders::_1));
//physical connection closed or drop.
_io->set_close_listener(std::bind(&MainWindow::OnClosed,this,_1));
//physical connection fail to establish.
_io->set_fail_listener(std::bind(&MainWindow::OnFailed,this));
connect(this,SIGNAL(RequestAddListItem(QListWidgetItem*)),this,SLOT(AddListItem(QListWidgetItem*)));
}
```
### Managing connection state
We have several connection listeners for connection events.
First we want to send login message once we're connected, get the default `socket` from `client` to do that.
```C++
void MainWindow::OnConnected()
{
QByteArray bytes = m_name.toUtf8();
std::string nickName(bytes.data(),bytes.length());
_io->socket()->emit("add user", nickName);
}
```
Then if connection is closed or failed, we need to restore to the UI before connect.
```C++
void MainWindow::OnClosed(client::close_reason const& reason)
{
//restore UI to pre-login state
}
void MainWindow::OnFailed()
{
//restore UI to pre-login state
}
```
If `MainWindow` is exit, we need to clear event bindings and listeners.
the `sio::client` object will be destruct by `unique_ptr`
```C++
MainWindow::~MainWindow()
{
_io->socket()->off_all();
_io->socket()->off_error();
delete ui;
}
```
### Handle socket.io events
We'll need to handle socket.io events in our functions bind to socket.io events.
For example, we need to show received messages to the `listView`
```C++
void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string msg = data->get_map()["message"]->get_string();
std::string name = data->get_map()["username"]->get_string();
QString label = QString::fromUtf8(name.data(),name.length());
label.append(':');
label.append(QString::fromUtf8(msg.data(),msg.length()));
QListWidgetItem *item= new QListWidgetItem(label);
//emit RequestAddListItem signal
//so that 'AddListItem' will be executed in UI thread.
Q_EMIT RequestAddListItem(item);
}
}
```
### Sending chat message
When `sendBtn` is clicked, we need to send the text in `messageEdit` to chatroom.
Add code to `SendBtnClicked()`:
```C++
void MainWindow::SendBtnClicked()
{
QLineEdit* messageEdit = this->findChild<QLineEdit*>("messageEdit");
QString text = messageEdit->text();
if(text.length()>0)
{
QByteArray bytes = text.toUtf8();
std::string msg(bytes.data(),bytes.length());
_io->socket()->emit("new message",msg);//emit new message
text.append(":You");
QListWidgetItem *item = new QListWidgetItem(text);
item->setTextAlignment(Qt::AlignRight);
Q_EMIT RequestAddListItem(item);
messageEdit->clear();
}
}
```
### Further reading
You can run [Demo project](https://github.com/socketio/socket.io-client-cpp/tree/master/examples/QT/SioChatDemo) to have a closer look.
Before running, please follow the [instructions](../../README.md#with_cmake) to make the sioclient library.

View File

@@ -1 +0,0 @@
SioChatDemo.pro.user

View File

@@ -1,44 +0,0 @@
#-------------------------------------------------
#
# Project created by QtCreator 2015-03-30T19:25:23
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = SioChatDemo
TEMPLATE = app
CONFIG+=no_keywords
CONFIG+=c++11
SOURCES += main.cpp\
mainwindow.cpp \
nicknamedialog.cpp
HEADERS += mainwindow.h \
nicknamedialog.h
FORMS += mainwindow.ui \
nicknamedialog.ui
CONFIG(debug, debug|release):DEFINES +=DEBUG=1
INCLUDEPATH += $$PWD/../../../build/include
DEPENDPATH += $$PWD/../../../build/lib
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lsioclient
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lsioclient
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lboost_random
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lboost_random
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lboost_system
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lboost_system
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lboost_date_time
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lboost_date_time

View File

@@ -1,4 +0,0 @@
osx/*
src/*
src
osx

View File

@@ -1,378 +0,0 @@
#===============================================================================
# Filename: boost.sh
# Author: Pete Goodliffe
# Copyright: (c) Copyright 2009 Pete Goodliffe
# Licence: Please feel free to use this, with attribution
# Modified version
#===============================================================================
#
# Builds a Boost framework for the iPhone.
# Creates a set of universal libraries that can be used on an iPhone and in the
# iPhone simulator. Then creates a pseudo-framework to make using boost in Xcode
# less painful.
#
# To configure the script, define:
# BOOST_LIBS: which libraries to build
# IPHONE_SDKVERSION: iPhone SDK version (e.g. 5.1)
#
# Then go get the source tar.bz of the boost you want to build, shove it in the
# same directory as this script, and run "./boost.sh". Grab a cuppa. And voila.
#===============================================================================
: ${BOOST_LIBS:="random regex graph random chrono thread signals filesystem system date_time"}
: ${IPHONE_SDKVERSION:=`xcodebuild -showsdks | grep iphoneos | egrep "[[:digit:]]+\.[[:digit:]]+" -o | tail -1`}
: ${OSX_SDKVERSION:=10.8}
: ${XCODE_ROOT:=`xcode-select -print-path`}
: ${EXTRA_CPPFLAGS:="-DBOOST_AC_USE_PTHREADS -DBOOST_SP_USE_PTHREADS -std=c++11 -stdlib=libc++"}
# The EXTRA_CPPFLAGS definition works around a thread race issue in
# shared_ptr. I encountered this historically and have not verified that
# the fix is no longer required. Without using the posix thread primitives
# an invalid compare-and-swap ARM instruction (non-thread-safe) was used for the
# shared_ptr use count causing nasty and subtle bugs.
#
# Should perhaps also consider/use instead: -BOOST_SP_USE_PTHREADS
: ${TARBALLDIR:=`pwd`}
: ${SRCDIR:=`pwd`/src}
: ${IOSBUILDDIR:=`pwd`/ios/build}
: ${OSXBUILDDIR:=`pwd`/osx/build}
: ${PREFIXDIR:=`pwd`/ios/prefix}
: ${IOSFRAMEWORKDIR:=`pwd`/ios/framework}
: ${OSXFRAMEWORKDIR:=`pwd`/osx/framework}
: ${COMPILER:="clang++"}
: ${BOOST_VERSION:=1.55.0}
: ${BOOST_VERSION2:=1_55_0}
BOOST_TARBALL=$TARBALLDIR/boost_$BOOST_VERSION2.tar.bz2
BOOST_SRC=$SRCDIR/boost_${BOOST_VERSION2}
#===============================================================================
ARM_DEV_CMD="xcrun --sdk iphoneos"
SIM_DEV_CMD="xcrun --sdk iphonesimulator"
OSX_DEV_CMD="xcrun --sdk macosx"
ARM_COMBINED_LIB=$IOSBUILDDIR/lib_boost_arm.a
SIM_COMBINED_LIB=$IOSBUILDDIR/lib_boost_x86.a
#===============================================================================
#===============================================================================
# Functions
#===============================================================================
abort()
{
echo
echo "Aborted: $@"
exit 1
}
doneSection()
{
echo
echo "================================================================="
echo "Done"
echo
}
#===============================================================================
cleanEverythingReadyToStart()
{
echo Cleaning everything before we start to build...
rm -rf iphone-build iphonesim-build osx-build
rm -rf $IOSBUILDDIR
rm -rf $OSXBUILDDIR
rm -rf $PREFIXDIR
rm -rf $IOSFRAMEWORKDIR/$FRAMEWORK_NAME.framework
rm -rf $OSXFRAMEWORKDIR/$FRAMEWORK_NAME.framework
doneSection
}
#===============================================================================
downloadBoost()
{
if [ ! -s $TARBALLDIR/boost_${BOOST_VERSION2}.tar.bz2 ]; then
echo "Downloading boost ${BOOST_VERSION}"
curl -L -o $TARBALLDIR/boost_${BOOST_VERSION2}.tar.bz2 http://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION}/boost_${BOOST_VERSION2}.tar.bz2/download
fi
doneSection
}
#===============================================================================
unpackBoost()
{
[ -f "$BOOST_TARBALL" ] || abort "Source tarball missing."
echo Unpacking boost into $SRCDIR...
[ -d $SRCDIR ] || mkdir -p $SRCDIR
[ -d $BOOST_SRC ] || ( cd $SRCDIR; tar xfj $BOOST_TARBALL )
[ -d $BOOST_SRC ] && echo " ...unpacked as $BOOST_SRC"
doneSection
}
#===============================================================================
restoreBoost()
{
cp $BOOST_SRC/tools/build/v2/user-config.jam-bk $BOOST_SRC/tools/build/v2/user-config.jam
}
#===============================================================================
updateBoost()
{
echo Updating boost into $BOOST_SRC...
cp $BOOST_SRC/tools/build/v2/user-config.jam $BOOST_SRC/tools/build/v2/user-config.jam-bk
cat >> $BOOST_SRC/tools/build/v2/user-config.jam <<EOF
using darwin : ${IPHONE_SDKVERSION}~iphone
: $XCODE_ROOT/Toolchains/XcodeDefault.xctoolchain/usr/bin/$COMPILER -arch armv6 -arch armv7 -arch armv7s -arch arm64 -fvisibility=hidden -fvisibility-inlines-hidden $EXTRA_CPPFLAGS
: <striper> <root>$XCODE_ROOT/Platforms/iPhoneOS.platform/Developer
: <architecture>arm <target-os>iphone
;
using darwin : ${IPHONE_SDKVERSION}~iphonesim
: $XCODE_ROOT/Toolchains/XcodeDefault.xctoolchain/usr/bin/$COMPILER -arch i386 -arch x86_64 -fvisibility=hidden -fvisibility-inlines-hidden $EXTRA_CPPFLAGS
: <striper> <root>$XCODE_ROOT/Platforms/iPhoneSimulator.platform/Developer
: <architecture>x86 <target-os>iphone
;
EOF
doneSection
}
#===============================================================================
inventMissingHeaders()
{
# These files are missing in the ARM iPhoneOS SDK, but they are in the simulator.
# They are supported on the device, so we copy them from x86 SDK to a staging area
# to use them on ARM, too.
echo Invent missing headers
cp $XCODE_ROOT/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDKVERSION}.sdk/usr/include/{crt_externs,bzlib}.h $BOOST_SRC
}
#===============================================================================
bootstrapBoost()
{
cd $BOOST_SRC
BOOST_LIBS_COMMA=$(echo $BOOST_LIBS | sed -e "s/ /,/g")
echo "Bootstrapping (with libs $BOOST_LIBS_COMMA)"
./bootstrap.sh --with-libraries=$BOOST_LIBS_COMMA
doneSection
}
#===============================================================================
buildBoostForIPhoneOS()
{
cd $BOOST_SRC
# Install this one so we can copy the includes for the frameworks...
./bjam -j16 --build-dir=iphone-build --stagedir=iphone-build/stage --prefix=$PREFIXDIR toolset=darwin architecture=arm target-os=iphone macosx-version=iphone-${IPHONE_SDKVERSION} define=_LITTLE_ENDIAN link=static stage
./bjam -j16 --build-dir=iphone-build --stagedir=iphone-build/stage --prefix=$PREFIXDIR toolset=darwin architecture=arm target-os=iphone macosx-version=iphone-${IPHONE_SDKVERSION} define=_LITTLE_ENDIAN link=static install
doneSection
./bjam -j16 --build-dir=iphonesim-build --stagedir=iphonesim-build/stage --toolset=darwin-${IPHONE_SDKVERSION}~iphonesim architecture=x86 target-os=iphone macosx-version=iphonesim-${IPHONE_SDKVERSION} link=static stage
doneSection
# ./b2 -j16 --build-dir=osx-build --stagedir=osx-build/stage toolset=clang cxxflags="-std=c++11 -stdlib=libc++ -arch i386 -arch x86_64" linkflags="-stdlib=libc++" link=static threading=multi stage
doneSection
}
#===============================================================================
scrunchAllLibsTogetherInOneLibPerPlatform()
{
cd $BOOST_SRC
mkdir -p $IOSBUILDDIR/armv6/obj
mkdir -p $IOSBUILDDIR/armv7/obj
mkdir -p $IOSBUILDDIR/armv7s/obj
mkdir -p $IOSBUILDDIR/arm64/obj
mkdir -p $IOSBUILDDIR/i386/obj
mkdir -p $IOSBUILDDIR/x86_64/obj
mkdir -p $OSXBUILDDIR/i386/obj
mkdir -p $OSXBUILDDIR/x86_64/obj
ALL_LIBS=""
echo Splitting all existing fat binaries...
for NAME in $BOOST_LIBS; do
ALL_LIBS="$ALL_LIBS libboost_$NAME.a"
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv6 -o $IOSBUILDDIR/armv6/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv7 -o $IOSBUILDDIR/armv7/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv7s -o $IOSBUILDDIR/armv7s/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin arm64 -o $IOSBUILDDIR/arm64/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphonesim-build/stage/lib/libboost_$NAME.a" -thin i386 -o $IOSBUILDDIR/i386/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphonesim-build/stage/lib/libboost_$NAME.a" -thin x86_64 -o $IOSBUILDDIR/x86_64/libboost_$NAME.a
$ARM_DEV_CMD lipo "osx-build/stage/lib/libboost_$NAME.a" -thin i386 -o $OSXBUILDDIR/i386/libboost_$NAME.a
$ARM_DEV_CMD lipo "osx-build/stage/lib/libboost_$NAME.a" -thin x86_64 -o $OSXBUILDDIR/x86_64/libboost_$NAME.a
done
echo "Decomposing each architecture's .a files"
for NAME in $ALL_LIBS; do
echo Decomposing $NAME...
(cd $IOSBUILDDIR/armv6/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/armv7/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/armv7s/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/arm64/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/i386/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/x86_64/obj; ar -x ../$NAME );
(cd $OSXBUILDDIR/i386/obj; ar -x ../$NAME );
(cd $OSXBUILDDIR/x86_64/obj; ar -x ../$NAME );
done
echo "Linking each architecture into an uberlib ($ALL_LIBS => libboost.a )"
rm $IOSBUILDDIR/*/libboost.a
echo ...armv6
(cd $IOSBUILDDIR/armv6; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...armv7
(cd $IOSBUILDDIR/armv7; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...armv7s
(cd $IOSBUILDDIR/armv7s; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...arm64
(cd $IOSBUILDDIR/arm64; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...i386
(cd $IOSBUILDDIR/i386; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...x86_64
(cd $IOSBUILDDIR/x86_64; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
rm $OSXBUILDDIR/*/libboost.a
echo ...osx-i386
(cd $OSXBUILDDIR/i386; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...x86_64
(cd $OSXBUILDDIR/x86_64; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
}
#===============================================================================
buildFramework()
{
: ${1:?}
FRAMEWORKDIR=$1
BUILDDIR=$2
VERSION_TYPE=Alpha
FRAMEWORK_NAME=boost
FRAMEWORK_VERSION=A
FRAMEWORK_CURRENT_VERSION=$BOOST_VERSION
FRAMEWORK_COMPATIBILITY_VERSION=$BOOST_VERSION
FRAMEWORK_BUNDLE=$FRAMEWORKDIR/$FRAMEWORK_NAME.framework
echo "Framework: Building $FRAMEWORK_BUNDLE from $BUILDDIR..."
rm -rf $FRAMEWORK_BUNDLE
echo "Framework: Setting up directories..."
mkdir -p $FRAMEWORK_BUNDLE
mkdir -p $FRAMEWORK_BUNDLE/Versions
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Resources
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Headers
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Documentation
echo "Framework: Creating symlinks..."
ln -s $FRAMEWORK_VERSION $FRAMEWORK_BUNDLE/Versions/Current
ln -s Versions/Current/Headers $FRAMEWORK_BUNDLE/Headers
ln -s Versions/Current/Resources $FRAMEWORK_BUNDLE/Resources
ln -s Versions/Current/Documentation $FRAMEWORK_BUNDLE/Documentation
ln -s Versions/Current/$FRAMEWORK_NAME $FRAMEWORK_BUNDLE/$FRAMEWORK_NAME
FRAMEWORK_INSTALL_NAME=$FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/$FRAMEWORK_NAME
echo "Lipoing library into $FRAMEWORK_INSTALL_NAME..."
$ARM_DEV_CMD lipo -create $BUILDDIR/*/libboost.a -o "$FRAMEWORK_INSTALL_NAME" || abort "Lipo $1 failed"
echo "Framework: Copying includes..."
cp -r $PREFIXDIR/include/boost/* $FRAMEWORK_BUNDLE/Headers/
echo "Framework: Creating plist..."
cat > $FRAMEWORK_BUNDLE/Resources/Info.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${FRAMEWORK_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.boost</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${FRAMEWORK_CURRENT_VERSION}</string>
</dict>
</plist>
EOF
doneSection
}
#===============================================================================
# Execution starts here
#===============================================================================
mkdir -p $IOSBUILDDIR
cleanEverythingReadyToStart #may want to comment if repeatedly running during dev
restoreBoost
echo "BOOST_VERSION: $BOOST_VERSION"
echo "BOOST_LIBS: $BOOST_LIBS"
echo "BOOST_SRC: $BOOST_SRC"
echo "IOSBUILDDIR: $IOSBUILDDIR"
echo "OSXBUILDDIR: $OSXBUILDDIR"
echo "PREFIXDIR: $PREFIXDIR"
echo "IOSFRAMEWORKDIR: $IOSFRAMEWORKDIR"
echo "OSXFRAMEWORKDIR: $OSXFRAMEWORKDIR"
echo "IPHONE_SDKVERSION: $IPHONE_SDKVERSION"
echo "XCODE_ROOT: $XCODE_ROOT"
echo "COMPILER: $COMPILER"
echo
downloadBoost
unpackBoost
inventMissingHeaders
bootstrapBoost
updateBoost
buildBoostForIPhoneOS
scrunchAllLibsTogetherInOneLibPerPlatform
buildFramework $IOSFRAMEWORKDIR $IOSBUILDDIR
buildFramework $OSXFRAMEWORKDIR $OSXBUILDDIR
restoreBoost
echo "Completed successfully"
#===============================================================================

View File

@@ -1,11 +0,0 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@@ -1,291 +0,0 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <functional>
#include <mutex>
#include <cstdlib>
#define kURL "ws://localhost:3000"
#ifdef WIN32
#define BIND_EVENT(IO,EV,FN) \
do{ \
socket::event_listener_aux l = FN;\
IO->on(EV,l);\
} while(0)
#else
#define BIND_EVENT(IO,EV,FN) \
IO->on(EV,FN)
#endif
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
_io(new client()),
m_typingItem(NULL),
m_dialog()
{
ui->setupUi(this);
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
socket::ptr sock = _io->socket();
BIND_EVENT(sock,"new message",std::bind(&MainWindow::OnNewMessage,this,_1,_2,_3,_4));
BIND_EVENT(sock,"user joined",std::bind(&MainWindow::OnUserJoined,this,_1,_2,_3,_4));
BIND_EVENT(sock,"user left",std::bind(&MainWindow::OnUserLeft,this,_1,_2,_3,_4));
BIND_EVENT(sock,"typing",std::bind(&MainWindow::OnTyping,this,_1,_2,_3,_4));
BIND_EVENT(sock,"stop typing",std::bind(&MainWindow::OnStopTyping,this,_1,_2,_3,_4));
BIND_EVENT(sock,"login",std::bind(&MainWindow::OnLogin,this,_1,_2,_3,_4));
_io->set_socket_open_listener(std::bind(&MainWindow::OnConnected,this,std::placeholders::_1));
_io->set_close_listener(std::bind(&MainWindow::OnClosed,this,_1));
_io->set_fail_listener(std::bind(&MainWindow::OnFailed,this));
connect(this,SIGNAL(RequestAddListItem(QListWidgetItem*)),this,SLOT(AddListItem(QListWidgetItem*)));
connect(this,SIGNAL(RequestRemoveListItem(QListWidgetItem*)),this,SLOT(RemoveListItem(QListWidgetItem*)));
connect(this,SIGNAL(RequestToggleInputs(bool)),this,SLOT(ToggleInputs(bool)));
}
MainWindow::~MainWindow()
{
_io->socket()->off_all();
_io->socket()->off_error();
delete ui;
}
void MainWindow::SendBtnClicked()
{
QLineEdit* messageEdit = this->findChild<QLineEdit*>("messageEdit");
QString text = messageEdit->text();
if(text.length()>0)
{
QByteArray bytes = text.toUtf8();
std::string msg(bytes.data(),bytes.length());
_io->socket()->emit("new message",msg);
text.append(" : You");
QListWidgetItem *item = new QListWidgetItem(text);
item->setTextAlignment(Qt::AlignRight);
Q_EMIT RequestAddListItem(item);
messageEdit->clear();
}
}
void MainWindow::OnMessageReturn()
{
this->SendBtnClicked();
}
void MainWindow::ShowLoginDialog()
{
m_dialog.reset(new NicknameDialog(this));
connect(m_dialog.get(),SIGNAL(accepted()),this,SLOT(NicknameAccept()));
connect(m_dialog.get(),SIGNAL(rejected()),this,SLOT(NicknameCancelled()));
m_dialog->exec();
}
void MainWindow::showEvent(QShowEvent *event)
{
ShowLoginDialog();
}
void MainWindow::TypingStop()
{
m_timer.reset();
_io->socket()->emit("stop typing");
}
void MainWindow::TypingChanged()
{
if(m_timer&&m_timer->isActive())
{
m_timer->stop();
}
else
{
_io->socket()->emit("typing");
}
m_timer.reset(new QTimer(this));
connect(m_timer.get(),SIGNAL(timeout()),this,SLOT(TypingStop()));
m_timer->setSingleShot(true);
m_timer->start(1000);
}
void MainWindow::NicknameAccept()
{
m_name = m_dialog->getNickname();
if(m_name.length()>0)
{
_io->connect(kURL);
}
}
void MainWindow::NicknameCancelled()
{
QApplication::exit();
}
void MainWindow::AddListItem(QListWidgetItem* item)
{
this->findChild<QListWidget*>("listView")->addItem(item);
}
void MainWindow::RemoveListItem(QListWidgetItem* item)
{
QListWidget* list = this->findChild<QListWidget*>("listView");
int row = list->row(item);
delete list->takeItem(row);
}
void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string msg = data->get_map()["message"]->get_string();
std::string username = data->get_map()["username"]->get_string();
QString label = QString::fromUtf8(username.data(),username.length());
label.append(" : ");
label.append(QString::fromUtf8(msg.data(),msg.length()));
QListWidgetItem *item= new QListWidgetItem(label);
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string name = data->get_map()["username"]->get_string();
int numUser = data->get_map()["numUsers"]->get_int();
QString label = QString::fromUtf8(name.data(),name.length());
bool plural = numUser != 1;
label.append(" joined\n");
label.append(plural?"there are ":"there's ");
QString digits;
while(numUser>=10)
{
digits.insert(0,QChar((numUser%10)+'0'));
numUser/=10;
}
digits.insert(0,QChar(numUser+'0'));
label.append(digits);
label.append(plural?" participants":" participant");
QListWidgetItem *item= new QListWidgetItem(label);
item->setTextAlignment(Qt::AlignHCenter);
QFont font;
font.setPointSize(9);
item->setFont(font);
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string name = data->get_map()["username"]->get_string();
int numUser = data->get_map()["numUsers"]->get_int();
QString label = QString::fromUtf8(name.data(),name.length());
bool plural = numUser != 1;
label.append(" left\n");
label.append(plural?"there are ":"there's ");
QString digits;
while(numUser>=10)
{
digits.insert(0,QChar((numUser%10)+'0'));
numUser/=10;
}
digits.insert(0,QChar(numUser+'0'));
label.append(digits);
label.append(plural?" participants":" participant");
QListWidgetItem *item= new QListWidgetItem(label);
item->setTextAlignment(Qt::AlignHCenter);
QFont font;
font.setPointSize(9);
item->setFont(font);
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(m_typingItem == NULL)
{
std::string name = data->get_map()["username"]->get_string();
QString label = QString::fromUtf8(name.data(),name.length());
label.append(" is typing...");
QListWidgetItem *item = new QListWidgetItem(label);
item->setTextColor(QColor(200,200,200,255));
m_typingItem = item;
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(m_typingItem != NULL)
{
Q_EMIT RequestRemoveListItem(m_typingItem);
m_typingItem = NULL;
}
}
void MainWindow::OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
Q_EMIT RequestToggleInputs(true);
int numUser = data->get_map()["numUsers"]->get_int();
QString digits;
bool plural = numUser !=1;
while(numUser>=10)
{
digits.insert(0,QChar((numUser%10)+'0'));
numUser/=10;
}
digits.insert(0,QChar(numUser+'0'));
digits.insert(0,plural?"there are ":"there's ");
digits.append(plural? " participants":" participant");
QListWidgetItem *item = new QListWidgetItem(digits);
item->setTextAlignment(Qt::AlignHCenter);
QFont font;
font.setPointSize(9);
item->setFont(font);
Q_EMIT RequestAddListItem(item);
}
void MainWindow::OnConnected(std::string const& nsp)
{
QByteArray bytes = m_name.toUtf8();
std::string nickName(bytes.data(),bytes.length());
_io->socket()->emit("add user", nickName);
}
void MainWindow::OnClosed(client::close_reason const& reason)
{
Q_EMIT RequestToggleInputs(false);
}
void MainWindow::OnFailed()
{
Q_EMIT RequestToggleInputs(false);
}
void MainWindow::ToggleInputs(bool loginOrNot)
{
if(loginOrNot)//already login
{
this->findChild<QWidget*>("messageEdit")->setEnabled(true);
this->findChild<QWidget*>("listView")->setEnabled(true);
// this->findChild<QWidget*>("sendBtn")->setEnabled(true);
}
else
{
this->findChild<QWidget*>("messageEdit")->setEnabled(false);
this->findChild<QWidget*>("listView")->setEnabled(false);
// this->findChild<QWidget*>("sendBtn")->setEnabled(false);
ShowLoginDialog();
}
}

View File

@@ -1,68 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QListWidget>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QTimer>
#include <sio_client.h>
#include "nicknamedialog.h"
namespace Ui {
class MainWindow;
}
using namespace sio;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public Q_SLOTS:
void SendBtnClicked();
void TypingChanged();
void OnMessageReturn();
protected:
void showEvent(QShowEvent* event);
Q_SIGNALS:
void RequestAddListItem(QListWidgetItem *item);
void RequestRemoveListItem(QListWidgetItem *item);
void RequestToggleInputs(bool loginOrNot);
private Q_SLOTS:
void AddListItem(QListWidgetItem *item);
void RemoveListItem(QListWidgetItem *item);
void ToggleInputs(bool loginOrNot);
void TypingStop();
void NicknameAccept();
void NicknameCancelled();
private:
void OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnConnected(std::string const& nsp);
void OnClosed(client::close_reason const& reason);
void OnFailed();
void ShowLoginDialog();
Ui::MainWindow *ui;
std::unique_ptr<client> _io;
std::unique_ptr<NicknameDialog> m_dialog;
QString m_name;
std::unique_ptr<QTimer> m_timer;
QListWidgetItem *m_typingItem;
};
#endif // MAINWINDOW_H

View File

@@ -1,131 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>549</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Socket.IO Chat</string>
</property>
<property name="unifiedTitleAndToolBarOnMac">
<bool>false</bool>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QListWidget" name="listView">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>8</x>
<y>11</y>
<width>350</width>
<height>461</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
<widget class="QLineEdit" name="messageEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>8</x>
<y>480</y>
<width>350</width>
<height>21</height>
</rect>
</property>
<property name="placeholderText">
<string>Type here...</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections>
<connection>
<sender>messageEdit</sender>
<signal>returnPressed()</signal>
<receiver>MainWindow</receiver>
<slot>OnMessageReturn()</slot>
<hints>
<hint type="sourcelabel">
<x>116</x>
<y>524</y>
</hint>
<hint type="destinationlabel">
<x>30</x>
<y>545</y>
</hint>
</hints>
</connection>
<connection>
<sender>messageEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>MainWindow</receiver>
<slot>TypingChanged()</slot>
<hints>
<hint type="sourcelabel">
<x>73</x>
<y>531</y>
</hint>
<hint type="destinationlabel">
<x>70</x>
<y>510</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>SendBtnClicked(bool)</slot>
<slot>TypingChanged()</slot>
<slot>LoginClicked()</slot>
<slot>OnMessageReturn()</slot>
<slot>SendBtnClicked()</slot>
</slots>
</ui>

View File

@@ -1,38 +0,0 @@
#include "nicknamedialog.h"
#include "ui_nicknamedialog.h"
#include <QTimer>
NicknameDialog::NicknameDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NicknameDialog)
{
ui->setupUi(this);
}
NicknameDialog::~NicknameDialog()
{
delete ui;
}
const QString& NicknameDialog::getNickname() const
{
return m_nickName;
}
void NicknameDialog::exitApp()
{
QApplication::quit();
}
void NicknameDialog::closeEvent(QCloseEvent *event)
{
QTimer::singleShot(0,this,SLOT(exitApp()));
}
void NicknameDialog::accept()
{
if(this->findChild<QLineEdit*>("nicknameEdit")->text().length()>0)
{
m_nickName = this->findChild<QLineEdit*>("nicknameEdit")->text();
done(QDialog::Accepted);
}
}

View File

@@ -1,28 +0,0 @@
#ifndef NICKNAMEDIALOG_H
#define NICKNAMEDIALOG_H
#include <QDialog>
namespace Ui {
class NicknameDialog;
}
class NicknameDialog : public QDialog
{
Q_OBJECT
public Q_SLOTS:
void accept();
void exitApp();
public:
explicit NicknameDialog(QWidget *parent = 0);
~NicknameDialog();
const QString& getNickname() const;
void closeEvent(QCloseEvent *);
private:
Ui::NicknameDialog *ui;
QString m_nickName;
};
#endif // NICKNAMEDIALOG_H

View File

@@ -1,106 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NicknameDialog</class>
<widget class="QDialog" name="NicknameDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>151</height>
</rect>
</property>
<property name="windowTitle">
<string>Login</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>30</x>
<y>99</y>
<width>311</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLineEdit" name="nicknameEdit">
<property name="geometry">
<rect>
<x>27</x>
<y>60</y>
<width>311</width>
<height>20</height>
</rect>
</property>
<property name="placeholderText">
<string>Type nickname here...</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>44</x>
<y>23</y>
<width>271</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>What's your nickname?</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NicknameDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>298</x>
<y>97</y>
</hint>
<hint type="destinationlabel">
<x>298</x>
<y>108</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NicknameDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>210</x>
<y>125</y>
</hint>
<hint type="destinationlabel">
<x>202</x>
<y>84</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>checkAccept()</slot>
<slot>checkAcc()</slot>
</slots>
</ui>

View File

@@ -1,31 +0,0 @@
#SIOClient iOS chat room demo
## What is this
This is a iOS project run on xcode 6, you can use this single view application chatting in the official [chat room demo](https://github.com/Automattic/socket.io/tree/master/examples/chat).
## How to setup
Suppose you're using your Mac's shell, under your workspace folder, run
```shell
git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git
```
Step in the demo folder
```shell
cd ./socket.io-client-cpp/examples/iOS/SioChatDemo
```
you will see a shell script named `boost.sh` under folder `boost`,Run
```shell
cd ./boost
bash ./boost.sh
```
Please stand by with patient, this step will take about one or two hours depends on your network.
When done, open `SioChatDemo.xcodeproj` file in the parent folder with xcode.
Just compile and run the `SioChatDemo` target.
Now, if you have your chat room server run on your local machine, you can chat with device to device or device to web.
## Use sioclient as static lib on iOS
There's a target named `sioclient` in the Demo project, That is the exactly right config for buiding the `sioclient` as a static library on iOS.
With the static library file `libsioclient.a` and two exported headers `sio_client.h` and `sio_message.h`, you won't need to config anything again and again in your integrating projects.
## About the `boost.sh`
The `boost.sh` is copied from [boostmake_ios](https://github.com/alist/boostmake_ios),it is worked on my machine for boost 1.55.0
there're lot's versions of boost build shells, you can choose what you like.

View File

@@ -1 +0,0 @@
/**/xcuserdata/

View File

@@ -1,722 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
CE1D31A01AD5087800895150 /* sio_packet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE1D319E1AD5087800895150 /* sio_packet.cpp */; };
CE1D31A11AD5087800895150 /* sio_packet.h in Headers */ = {isa = PBXBuildFile; fileRef = CE1D319F1AD5087800895150 /* sio_packet.h */; };
CE2958691ACE4589000ABD30 /* sio_client_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE2958671ACE442F000ABD30 /* sio_client_impl.cpp */; };
CE29586A1ACE4592000ABD30 /* sio_socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE2958621ACE2CD6000ABD30 /* sio_socket.cpp */; };
CE3B3B401AC8F365003CEB94 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CE3B3B3F1AC8F365003CEB94 /* main.m */; };
CE3B3B431AC8F365003CEB94 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE3B3B421AC8F365003CEB94 /* AppDelegate.m */; };
CE3B3B4B1AC8F365003CEB94 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE3B3B4A1AC8F365003CEB94 /* Images.xcassets */; };
CE3B3B4E1AC8F365003CEB94 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3B3B4C1AC8F365003CEB94 /* LaunchScreen.xib */; };
CE3B3B5A1AC8F365003CEB94 /* SioChatDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CE3B3B591AC8F365003CEB94 /* SioChatDemoTests.m */; };
CE3B3B651AC8F385003CEB94 /* CRViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CE3B3B641AC8F385003CEB94 /* CRViewController.mm */; };
CE3B3B751AC8F438003CEB94 /* libsioclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE3B3B6A1AC8F438003CEB94 /* libsioclient.a */; };
CE3B3E6D1AC8FE59003CEB94 /* libsioclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE3B3B6A1AC8F438003CEB94 /* libsioclient.a */; };
CE3B3E6F1AC9139B003CEB94 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE3B3E6E1AC9139B003CEB94 /* Main.storyboard */; };
CE3B3E8C1ACA5446003CEB94 /* sio_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3B3E871ACA5446003CEB94 /* sio_client.cpp */; };
CE3B3E8D1ACA5446003CEB94 /* sio_client.h in Headers */ = {isa = PBXBuildFile; fileRef = CE3B3E881ACA5446003CEB94 /* sio_client.h */; };
CE3B3E8E1ACA5446003CEB94 /* sio_message.h in Headers */ = {isa = PBXBuildFile; fileRef = CE3B3E891ACA5446003CEB94 /* sio_message.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
CE3B3B541AC8F365003CEB94 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = CE3B3B321AC8F365003CEB94 /* Project object */;
proxyType = 1;
remoteGlobalIDString = CE3B3B391AC8F365003CEB94;
remoteInfo = SioChatDemo;
};
CE3B3B761AC8F438003CEB94 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = CE3B3B321AC8F365003CEB94 /* Project object */;
proxyType = 1;
remoteGlobalIDString = CE3B3B691AC8F438003CEB94;
remoteInfo = sioclient;
};
CE3B3E6B1AC8FE54003CEB94 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = CE3B3B321AC8F365003CEB94 /* Project object */;
proxyType = 1;
remoteGlobalIDString = CE3B3B691AC8F438003CEB94;
remoteInfo = sioclient;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
CE3B3B681AC8F438003CEB94 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
CE1D319E1AD5087800895150 /* sio_packet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sio_packet.cpp; sourceTree = "<group>"; };
CE1D319F1AD5087800895150 /* sio_packet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sio_packet.h; sourceTree = "<group>"; };
CE2958621ACE2CD6000ABD30 /* sio_socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sio_socket.cpp; sourceTree = "<group>"; };
CE2958631ACE2CD6000ABD30 /* sio_socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sio_socket.h; sourceTree = "<group>"; };
CE2958661ACE2CDD000ABD30 /* sio_client_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sio_client_impl.h; sourceTree = "<group>"; };
CE2958671ACE442F000ABD30 /* sio_client_impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sio_client_impl.cpp; sourceTree = "<group>"; };
CE3B3B3A1AC8F365003CEB94 /* SioChatDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SioChatDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
CE3B3B3E1AC8F365003CEB94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CE3B3B3F1AC8F365003CEB94 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
CE3B3B411AC8F365003CEB94 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
CE3B3B421AC8F365003CEB94 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
CE3B3B4A1AC8F365003CEB94 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
CE3B3B4D1AC8F365003CEB94 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
CE3B3B531AC8F365003CEB94 /* SioChatDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SioChatDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
CE3B3B581AC8F365003CEB94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CE3B3B591AC8F365003CEB94 /* SioChatDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SioChatDemoTests.m; sourceTree = "<group>"; };
CE3B3B631AC8F385003CEB94 /* CRViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CRViewController.h; sourceTree = "<group>"; };
CE3B3B641AC8F385003CEB94 /* CRViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CRViewController.mm; sourceTree = "<group>"; };
CE3B3B6A1AC8F438003CEB94 /* libsioclient.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsioclient.a; sourceTree = BUILT_PRODUCTS_DIR; };
CE3B3B741AC8F438003CEB94 /* sioclientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = sioclientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
CE3B3B7A1AC8F438003CEB94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CE3B3E6E1AC9139B003CEB94 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
CE3B3E871ACA5446003CEB94 /* sio_client.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sio_client.cpp; sourceTree = "<group>"; };
CE3B3E881ACA5446003CEB94 /* sio_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sio_client.h; sourceTree = "<group>"; };
CE3B3E891ACA5446003CEB94 /* sio_message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sio_message.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
CE3B3B371AC8F365003CEB94 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CE3B3E6D1AC8FE59003CEB94 /* libsioclient.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B501AC8F365003CEB94 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B671AC8F438003CEB94 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B711AC8F438003CEB94 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CE3B3B751AC8F438003CEB94 /* libsioclient.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
CE2958651ACE2CDD000ABD30 /* internal */ = {
isa = PBXGroup;
children = (
CE1D319E1AD5087800895150 /* sio_packet.cpp */,
CE1D319F1AD5087800895150 /* sio_packet.h */,
CE2958661ACE2CDD000ABD30 /* sio_client_impl.h */,
CE2958671ACE442F000ABD30 /* sio_client_impl.cpp */,
);
path = internal;
sourceTree = "<group>";
};
CE3B3B311AC8F365003CEB94 = {
isa = PBXGroup;
children = (
CE3B3E861ACA5446003CEB94 /* src */,
CE3B3B3C1AC8F365003CEB94 /* SioChatDemo */,
CE3B3B561AC8F365003CEB94 /* SioChatDemoTests */,
CE3B3B6B1AC8F438003CEB94 /* sioclient */,
CE3B3B781AC8F438003CEB94 /* sioclientTests */,
CE3B3B3B1AC8F365003CEB94 /* Products */,
);
sourceTree = "<group>";
};
CE3B3B3B1AC8F365003CEB94 /* Products */ = {
isa = PBXGroup;
children = (
CE3B3B3A1AC8F365003CEB94 /* SioChatDemo.app */,
CE3B3B531AC8F365003CEB94 /* SioChatDemoTests.xctest */,
CE3B3B6A1AC8F438003CEB94 /* libsioclient.a */,
CE3B3B741AC8F438003CEB94 /* sioclientTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
CE3B3B3C1AC8F365003CEB94 /* SioChatDemo */ = {
isa = PBXGroup;
children = (
CE3B3E6E1AC9139B003CEB94 /* Main.storyboard */,
CE3B3B411AC8F365003CEB94 /* AppDelegate.h */,
CE3B3B421AC8F365003CEB94 /* AppDelegate.m */,
CE3B3B631AC8F385003CEB94 /* CRViewController.h */,
CE3B3B641AC8F385003CEB94 /* CRViewController.mm */,
CE3B3B4A1AC8F365003CEB94 /* Images.xcassets */,
CE3B3B4C1AC8F365003CEB94 /* LaunchScreen.xib */,
CE3B3B3D1AC8F365003CEB94 /* Supporting Files */,
);
path = SioChatDemo;
sourceTree = "<group>";
};
CE3B3B3D1AC8F365003CEB94 /* Supporting Files */ = {
isa = PBXGroup;
children = (
CE3B3B3E1AC8F365003CEB94 /* Info.plist */,
CE3B3B3F1AC8F365003CEB94 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
CE3B3B561AC8F365003CEB94 /* SioChatDemoTests */ = {
isa = PBXGroup;
children = (
CE3B3B591AC8F365003CEB94 /* SioChatDemoTests.m */,
CE3B3B571AC8F365003CEB94 /* Supporting Files */,
);
path = SioChatDemoTests;
sourceTree = "<group>";
};
CE3B3B571AC8F365003CEB94 /* Supporting Files */ = {
isa = PBXGroup;
children = (
CE3B3B581AC8F365003CEB94 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
CE3B3B6B1AC8F438003CEB94 /* sioclient */ = {
isa = PBXGroup;
children = (
);
path = sioclient;
sourceTree = "<group>";
};
CE3B3B781AC8F438003CEB94 /* sioclientTests */ = {
isa = PBXGroup;
children = (
CE3B3B791AC8F438003CEB94 /* Supporting Files */,
);
path = sioclientTests;
sourceTree = "<group>";
};
CE3B3B791AC8F438003CEB94 /* Supporting Files */ = {
isa = PBXGroup;
children = (
CE3B3B7A1AC8F438003CEB94 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
CE3B3E861ACA5446003CEB94 /* src */ = {
isa = PBXGroup;
children = (
CE2958651ACE2CDD000ABD30 /* internal */,
CE2958621ACE2CD6000ABD30 /* sio_socket.cpp */,
CE2958631ACE2CD6000ABD30 /* sio_socket.h */,
CE3B3E871ACA5446003CEB94 /* sio_client.cpp */,
CE3B3E881ACA5446003CEB94 /* sio_client.h */,
CE3B3E891ACA5446003CEB94 /* sio_message.h */,
);
name = src;
path = ../../../src;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
CE3B3E681AC8F511003CEB94 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
CE3B3E8E1ACA5446003CEB94 /* sio_message.h in Headers */,
CE1D31A11AD5087800895150 /* sio_packet.h in Headers */,
CE3B3E8D1ACA5446003CEB94 /* sio_client.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
CE3B3B391AC8F365003CEB94 /* SioChatDemo */ = {
isa = PBXNativeTarget;
buildConfigurationList = CE3B3B5D1AC8F365003CEB94 /* Build configuration list for PBXNativeTarget "SioChatDemo" */;
buildPhases = (
CE3B3B361AC8F365003CEB94 /* Sources */,
CE3B3B371AC8F365003CEB94 /* Frameworks */,
CE3B3B381AC8F365003CEB94 /* Resources */,
);
buildRules = (
);
dependencies = (
CE3B3E6C1AC8FE54003CEB94 /* PBXTargetDependency */,
);
name = SioChatDemo;
productName = SioChatDemo;
productReference = CE3B3B3A1AC8F365003CEB94 /* SioChatDemo.app */;
productType = "com.apple.product-type.application";
};
CE3B3B521AC8F365003CEB94 /* SioChatDemoTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = CE3B3B601AC8F365003CEB94 /* Build configuration list for PBXNativeTarget "SioChatDemoTests" */;
buildPhases = (
CE3B3B4F1AC8F365003CEB94 /* Sources */,
CE3B3B501AC8F365003CEB94 /* Frameworks */,
CE3B3B511AC8F365003CEB94 /* Resources */,
);
buildRules = (
);
dependencies = (
CE3B3B551AC8F365003CEB94 /* PBXTargetDependency */,
);
name = SioChatDemoTests;
productName = SioChatDemoTests;
productReference = CE3B3B531AC8F365003CEB94 /* SioChatDemoTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
CE3B3B691AC8F438003CEB94 /* sioclient */ = {
isa = PBXNativeTarget;
buildConfigurationList = CE3B3B7B1AC8F438003CEB94 /* Build configuration list for PBXNativeTarget "sioclient" */;
buildPhases = (
CE3B3B661AC8F438003CEB94 /* Sources */,
CE3B3B671AC8F438003CEB94 /* Frameworks */,
CE3B3B681AC8F438003CEB94 /* CopyFiles */,
CE3B3E681AC8F511003CEB94 /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = sioclient;
productName = sioclient;
productReference = CE3B3B6A1AC8F438003CEB94 /* libsioclient.a */;
productType = "com.apple.product-type.library.static";
};
CE3B3B731AC8F438003CEB94 /* sioclientTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = CE3B3B7E1AC8F438003CEB94 /* Build configuration list for PBXNativeTarget "sioclientTests" */;
buildPhases = (
CE3B3B701AC8F438003CEB94 /* Sources */,
CE3B3B711AC8F438003CEB94 /* Frameworks */,
CE3B3B721AC8F438003CEB94 /* Resources */,
);
buildRules = (
);
dependencies = (
CE3B3B771AC8F438003CEB94 /* PBXTargetDependency */,
);
name = sioclientTests;
productName = sioclientTests;
productReference = CE3B3B741AC8F438003CEB94 /* sioclientTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
CE3B3B321AC8F365003CEB94 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = "Melo Yao";
TargetAttributes = {
CE3B3B391AC8F365003CEB94 = {
CreatedOnToolsVersion = 6.1.1;
};
CE3B3B521AC8F365003CEB94 = {
CreatedOnToolsVersion = 6.1.1;
TestTargetID = CE3B3B391AC8F365003CEB94;
};
CE3B3B691AC8F438003CEB94 = {
CreatedOnToolsVersion = 6.1.1;
};
CE3B3B731AC8F438003CEB94 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = CE3B3B351AC8F365003CEB94 /* Build configuration list for PBXProject "SioChatDemo" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = CE3B3B311AC8F365003CEB94;
productRefGroup = CE3B3B3B1AC8F365003CEB94 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CE3B3B391AC8F365003CEB94 /* SioChatDemo */,
CE3B3B521AC8F365003CEB94 /* SioChatDemoTests */,
CE3B3B691AC8F438003CEB94 /* sioclient */,
CE3B3B731AC8F438003CEB94 /* sioclientTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
CE3B3B381AC8F365003CEB94 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CE3B3E6F1AC9139B003CEB94 /* Main.storyboard in Resources */,
CE3B3B4E1AC8F365003CEB94 /* LaunchScreen.xib in Resources */,
CE3B3B4B1AC8F365003CEB94 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B511AC8F365003CEB94 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B721AC8F438003CEB94 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
CE3B3B361AC8F365003CEB94 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CE3B3B431AC8F365003CEB94 /* AppDelegate.m in Sources */,
CE3B3B401AC8F365003CEB94 /* main.m in Sources */,
CE3B3B651AC8F385003CEB94 /* CRViewController.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B4F1AC8F365003CEB94 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CE3B3B5A1AC8F365003CEB94 /* SioChatDemoTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B661AC8F438003CEB94 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CE29586A1ACE4592000ABD30 /* sio_socket.cpp in Sources */,
CE1D31A01AD5087800895150 /* sio_packet.cpp in Sources */,
CE2958691ACE4589000ABD30 /* sio_client_impl.cpp in Sources */,
CE3B3E8C1ACA5446003CEB94 /* sio_client.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
CE3B3B701AC8F438003CEB94 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
CE3B3B551AC8F365003CEB94 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = CE3B3B391AC8F365003CEB94 /* SioChatDemo */;
targetProxy = CE3B3B541AC8F365003CEB94 /* PBXContainerItemProxy */;
};
CE3B3B771AC8F438003CEB94 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = CE3B3B691AC8F438003CEB94 /* sioclient */;
targetProxy = CE3B3B761AC8F438003CEB94 /* PBXContainerItemProxy */;
};
CE3B3E6C1AC8FE54003CEB94 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = CE3B3B691AC8F438003CEB94 /* sioclient */;
targetProxy = CE3B3E6B1AC8FE54003CEB94 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
CE3B3B4C1AC8F365003CEB94 /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
CE3B3B4D1AC8F365003CEB94 /* Base */,
);
name = LaunchScreen.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
CE3B3B5B1AC8F365003CEB94 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
CE3B3B5C1AC8F365003CEB94 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
CE3B3B5E1AC8F365003CEB94 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = SioChatDemo/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
CE3B3B5F1AC8F365003CEB94 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = SioChatDemo/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
CE3B3B611AC8F365003CEB94 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = SioChatDemoTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SioChatDemo.app/SioChatDemo";
};
name = Debug;
};
CE3B3B621AC8F365003CEB94 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = SioChatDemoTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SioChatDemo.app/SioChatDemo";
};
name = Release;
};
CE3B3B7C1AC8F438003CEB94 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SIOROOT)/lib/websocketpp",
"$(SIOROOT)/lib/rapidjson/include",
"$(SRCROOT)/boost/ios/prefix/include",
);
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/boost/ios/build/$(CURRENT_ARCH)/";
OTHER_LDFLAGS = "-ObjC";
OTHER_LIBTOOLFLAGS = "-lboost_random -lboost_date_time -lboost_system";
PRODUCT_NAME = "$(TARGET_NAME)";
SIOROOT = "$(SRCROOT)/../../../";
SKIP_INSTALL = YES;
};
name = Debug;
};
CE3B3B7D1AC8F438003CEB94 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SIOROOT)/lib/websocketpp",
"$(SIOROOT)/lib/rapidjson/include",
"$(SRCROOT)/boost/ios/prefix/include",
);
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/boost/ios/build/$(CURRENT_ARCH)/";
OTHER_LDFLAGS = "-ObjC";
OTHER_LIBTOOLFLAGS = "-lboost_random -lboost_date_time -lboost_system";
PRODUCT_NAME = "$(TARGET_NAME)";
SIOROOT = "$(SRCROOT)/../../../";
SKIP_INSTALL = YES;
};
name = Release;
};
CE3B3B7F1AC8F438003CEB94 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = sioclientTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
CE3B3B801AC8F438003CEB94 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = sioclientTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
CE3B3B351AC8F365003CEB94 /* Build configuration list for PBXProject "SioChatDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CE3B3B5B1AC8F365003CEB94 /* Debug */,
CE3B3B5C1AC8F365003CEB94 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CE3B3B5D1AC8F365003CEB94 /* Build configuration list for PBXNativeTarget "SioChatDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CE3B3B5E1AC8F365003CEB94 /* Debug */,
CE3B3B5F1AC8F365003CEB94 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CE3B3B601AC8F365003CEB94 /* Build configuration list for PBXNativeTarget "SioChatDemoTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CE3B3B611AC8F365003CEB94 /* Debug */,
CE3B3B621AC8F365003CEB94 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CE3B3B7B1AC8F438003CEB94 /* Build configuration list for PBXNativeTarget "sioclient" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CE3B3B7C1AC8F438003CEB94 /* Debug */,
CE3B3B7D1AC8F438003CEB94 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CE3B3B7E1AC8F438003CEB94 /* Build configuration list for PBXNativeTarget "sioclientTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CE3B3B7F1AC8F438003CEB94 /* Debug */,
CE3B3B801AC8F438003CEB94 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = CE3B3B321AC8F365003CEB94 /* Project object */;
}

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:SioChatDemo.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>F1CE472E-3AF3-4EDA-9B64-F9DD83C665BC</string>
<key>IDESourceControlProjectName</key>
<string>SioChatDemo</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>21C71CD5ED4B8C77974DDBAF056104F039A3B593</key>
<string>https://github.com/socketio/socket.io-client-cpp.git</string>
<key>7448E718372DB7A4B0D01E27524ED3F8AE51A902</key>
<string>https://github.com/miloyip/rapidjson.git</string>
<key>995AABD49A028E610D29FFC44D560D9DAC1EAC0B</key>
<string>https://github.com/zaphoyd/websocketpp.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>examples/iOS/SioChatDemo/SioChatDemo.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>21C71CD5ED4B8C77974DDBAF056104F039A3B593</key>
<string>../../../../..</string>
<key>7448E718372DB7A4B0D01E27524ED3F8AE51A902</key>
<string>../../../../..lib/rapidjson</string>
<key>995AABD49A028E610D29FFC44D560D9DAC1EAC0B</key>
<string>../../../../..lib/websocketpp</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/socketio/socket.io-client-cpp.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>21C71CD5ED4B8C77974DDBAF056104F039A3B593</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>7448E718372DB7A4B0D01E27524ED3F8AE51A902</string>
<key>IDESourceControlWCCName</key>
<string>rapidjson</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>21C71CD5ED4B8C77974DDBAF056104F039A3B593</string>
<key>IDESourceControlWCCName</key>
<string>sioclient</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>995AABD49A028E610D29FFC44D560D9DAC1EAC0B</string>
<key>IDESourceControlWCCName</key>
<string>websocketpp</string>
</dict>
</array>
</dict>
</plist>

View File

@@ -1,17 +0,0 @@
//
// AppDelegate.h
// SioChatDemo
//
// Created by Melo Yao on 3/30/15.
// Copyright (c) 2015 Melo Yao. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -1,45 +0,0 @@
//
// AppDelegate.m
// SioChatDemo
//
// Created by Melo Yao on 3/30/15.
// Copyright (c) 2015 Melo Yao. All rights reserved.
//
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end

View File

@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 Melo Yao. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SioChatDemo" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@@ -1,12 +0,0 @@
//
// CRViewController.h
// ChatRoom
//
// Created by Melo Yao on 3/30/15.
//
#import <UIKit/UIKit.h>
@interface CRViewController : UIViewController
@end

View File

@@ -1,453 +0,0 @@
//
// CRViewController.m
// ChatRoom
//
// Created by Melo Yao on 3/30/15.
//
#import "CRViewController.h"
#include "sio_client.h"
typedef enum MessageFlag
{
Message_System,
Message_Other,
Message_You
};
@interface MessageItem : NSObject
@property NSString* message;
@property MessageFlag flag; //0 system info, 1 other message, 2 your message
@end
@implementation MessageItem
@end
@interface CRViewController ()<UITableViewDataSource,UITableViewDelegate,NSURLConnectionDelegate,NSURLConnectionDataDelegate,UITextFieldDelegate>
{
sio::client *_io;
NSMutableArray *_receivedMessage;
NSMutableSet *_typingUsers;
NSString* _name;
NSInteger _userCount;
NSTimer* _inputTimer;
}
@property (weak, nonatomic) IBOutlet UILabel *infoLabel;
@property (weak, nonatomic) IBOutlet UILabel *typingLabel;
@property (strong, nonatomic) IBOutlet UIView *loginPage;
@property (weak, nonatomic) IBOutlet UITextField *nickName;
- (IBAction)onSend:(id)sender;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UIButton *sendBtn;
@property (weak, nonatomic) IBOutlet UITextField *messageField;
@property (weak, nonatomic) IBOutlet UIView *messageArea;
-(void)onNewMessage:(NSString*) message from:(NSString*) name;
-(void)onUserJoined:(NSString*)user participants:(NSInteger) num;
-(void)onUserLeft:(NSString*) user participants:(NSInteger) num;
-(void)onUserTyping:(NSString*) user;
-(void)onUserStopTyping:(NSString*) user;
-(void)onLogin:(NSInteger) numParticipants;
-(void)onConnected;
-(void)onDisconnected;
-(void) updateUser:(NSString*)user count:(NSInteger) num joinOrLeft:(BOOL) isJoin;
@end
using namespace std;
using namespace sio;
void OnNewMessage(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* msg = [NSString stringWithUTF8String:data->get_map()["message"]->get_string().data()];
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onNewMessage:msg from:user];
});
}
}
void OnTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserTyping:user];
});
}
}
void OnStopTyping(CFTypeRef ctrl,string const& name,sio::message::ptr const& data,bool needACK,sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserStopTyping:user];
});
}
}
void OnUserJoined(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
NSInteger num = data->get_map()["numUsers"]->get_int();
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserJoined:user participants:num];
});
}
}
void OnUserLeft(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSString* user = [NSString stringWithUTF8String:data->get_map()["username"]->get_string().data()];
NSInteger num = data->get_map()["numUsers"]->get_int();
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onUserLeft:user participants:num];
});
}
}
void OnLogin(CFTypeRef ctrl, string const& name, sio::message::ptr const& data, bool needACK, sio::message::list ackResp)
{
if(data->get_flag() == message::flag_object)
{
NSInteger num = data->get_map()["numUsers"]->get_int();
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onLogin:num];
});
}
}
void OnConnected(CFTypeRef ctrl,std::string nsp)
{
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onConnected];
});
}
void OnFailed(CFTypeRef ctrl)
{
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onDisconnected];
});
}
void OnClose(CFTypeRef ctrl,sio::client::close_reason const& reason)
{
dispatch_async(dispatch_get_main_queue(), ^{
[((__bridge CRViewController*)ctrl) onDisconnected];
});
}
@implementation CRViewController
-(void)awakeFromNib
{
_receivedMessage = [NSMutableArray array];
_typingUsers = [NSMutableSet set];
_io = new sio::client();
[self.loginPage setFrame:self.view.bounds];
[self.view addSubview:self.loginPage];
self.nickName.enabled = YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)viewWillAppear:(BOOL)animated
{
_io->set_socket_open_listener(std::bind(&OnConnected, (__bridge CFTypeRef)self,std::placeholders::_1));
_io->set_close_listener(std::bind(&OnClose, (__bridge CFTypeRef)self, std::placeholders::_1));
_io->set_fail_listener(std::bind(&OnFailed, (__bridge CFTypeRef)self));
}
-(void)keyboardWillShow:(NSNotification*)notification
{
CGFloat height = [[notification.userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
// Animate the current view out of the way
[UIView animateWithDuration:0.35 animations:^{
self.messageArea.transform = CGAffineTransformMakeTranslation(0, -height);
}];
}
-(void)keyboardWillHide {
[UIView animateWithDuration:0.35 animations:^{
self.messageArea.transform = CGAffineTransformIdentity;
}];
}
-(void)viewDidDisappear:(BOOL)animated
{
_io->socket()->off_all();
_io->set_open_listener(nullptr);
_io->set_close_listener(nullptr);
_io->close();
}
-(void)onNewMessage:(NSString*) message from:(NSString*) name
{
MessageItem *item = [[MessageItem alloc] init];
item.flag = [name isEqualToString:_name]?Message_You:Message_Other;
item.message = item.flag == Message_You? [NSString stringWithFormat:@"%@:%@",message,name]:[NSString stringWithFormat:@"%@:%@",name,message];
[_receivedMessage addObject:item];
[_tableView reloadData];
if(![_messageField isFirstResponder])
{
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_receivedMessage count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
-(void)onUserJoined:(NSString*)user participants:(NSInteger) num
{
_userCount = num;
[self updateUser:user count:num joinOrLeft:YES];
}
-(void)onUserLeft:(NSString*) user participants:(NSInteger) num
{
[_typingUsers removeObject:user];//protective removal.
[self updateTyping];
_userCount = num;
[self updateUser:user count:num joinOrLeft:NO];
}
-(void)onUserTyping:(NSString*) user
{
[_typingUsers addObject:user];
[self updateTyping];
}
-(void)onUserStopTyping:(NSString*) user
{
[_typingUsers removeObject:user];
[self updateTyping];
}
-(void)onLogin:(NSInteger) numParticipants
{
_name = _nickName.text;
_userCount = numParticipants;
[self.loginPage removeFromSuperview];
[self updateUser:nil count:_userCount joinOrLeft:YES];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)onSend:(id)sender {
if ([_messageField.text length]>0 && [_name length]>0) {
_io->socket()->emit("new message",[_messageField.text UTF8String]);
MessageItem *item = [[MessageItem alloc] init];
item.flag = Message_You;
item.message = [NSString stringWithFormat:@"%@:You",_messageField.text];
[_receivedMessage addObject:item];
[_tableView reloadData];
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_receivedMessage count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
self.messageField.text = nil;
[self.messageField resignFirstResponder];
}
-(void)onConnected
{
_io->socket()->emit("add user", [self.nickName.text UTF8String]);
}
-(void)onDisconnected
{
if([self.loginPage superview] == nil)
{
[self.view addSubview:self.loginPage];
}
self.nickName.enabled = YES;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
-(void) updateUser:(NSString*)user count:(NSInteger) num joinOrLeft:(BOOL) isJoin
{
_userCount = num;
MessageItem *item = [[MessageItem alloc] init];
item.flag = Message_System;
if (user) {
item.message = [NSString stringWithFormat:@"%@ %@\n%@",user,isJoin?@"joined":@"left",num==1?@"there's 1 participant":[NSString stringWithFormat:@"there are %ld participants",num]];
}
else
{
item.message = [NSString stringWithFormat:@"Welcome to Socket.IO Chat-\n%@",num==1?@"there's 1 participant":[NSString stringWithFormat:@"there are %ld participants",num]];
}
[_receivedMessage addObject:item];
[_tableView reloadData];
if(![_messageField isFirstResponder])
{
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_receivedMessage count]-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
-(void) inputTimeout
{
_inputTimer = nil;
_io->socket()->emit("stop typing", "");
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField == self.messageField)
{
if(_inputTimer.valid)
{
[_inputTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
else
{
_io->socket()->emit("typing", "");
_inputTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(inputTimeout) userInfo:nil repeats:NO];
}
}
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_receivedMessage count];
}
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Msg"];
MessageItem* item = [_receivedMessage objectAtIndex:indexPath.row];
cell.textLabel.text = item.message;
switch (item.flag) {
case Message_System:
cell.textLabel.textAlignment = NSTextAlignmentCenter;
[cell.textLabel setFont:[UIFont fontWithName:[cell.textLabel.font fontName] size:12]];
break;
case Message_Other:
[cell.textLabel setFont:[UIFont fontWithName:[cell.textLabel.font fontName] size:15]];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
break;
case Message_You:
[cell.textLabel setFont:[UIFont fontWithName:[cell.textLabel.font fontName] size:15]];
cell.textLabel.textAlignment = NSTextAlignmentRight;
break;
default:
break;
}
return cell;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if ([self.messageField isFirstResponder]) {
[self.messageField resignFirstResponder];
}
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.nickName) {
if ([self.nickName.text length] > 0) {
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
socket::ptr socket = _io->socket();
socket->on("new message", std::bind(&OnNewMessage, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("typing", std::bind(&OnTyping, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("stop typing", std::bind(&OnStopTyping, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("user joined", std::bind(&OnUserJoined, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("user left", std::bind(&OnUserLeft, (__bridge CFTypeRef)self, _1,_2,_3,_4));
socket->on("login", std::bind(&OnLogin, (__bridge CFTypeRef)self, _1,_2,_3,_4));
_io->connect("ws://localhost:3000");
self.nickName.enabled = NO;
}
}
else if(textField == self.messageField)
{
[self onSend:textField];
}
return YES;
}
-(void)updateTyping
{
NSString* typingMsg = nil;
NSString* name = [_typingUsers anyObject];
if (name) {
if([_typingUsers count]>1)
{
typingMsg = [NSString stringWithFormat:@"%@ and %ld more are typing",name,[_typingUsers count]];
}
else
{
typingMsg =[NSString stringWithFormat:@"%@ is typing",name];
}
}
self.typingLabel.text = typingMsg;
}
- (void)dealloc
{
delete _io;
}
@end

View File

@@ -1,68 +0,0 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.socketio.melode.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -1,127 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="FVA-Ip-uIE">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="4B5-FI-8YR">
<objects>
<viewController id="FVA-Ip-uIE" customClass="CRViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="j56-Sc-Uve">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="Zd9-dA-z1N">
<rect key="frame" x="0.0" y="20" width="320" height="396"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Msg" textLabel="c7q-ly-Hf0" style="IBUITableViewCellStyleDefault" id="13Y-SW-wci">
<rect key="frame" x="0.0" y="22" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="13Y-SW-wci" id="CkG-vX-yh5">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" id="c7q-ly-Hf0">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="18"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="FVA-Ip-uIE" id="gbf-ws-uXu"/>
<outlet property="delegate" destination="FVA-Ip-uIE" id="Y5j-aM-c5q"/>
</connections>
</tableView>
<view contentMode="scaleToFill" id="yh5-ME-DKM">
<rect key="frame" x="0.0" y="416" width="320" height="64"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Type here..." minimumFontSize="17" id="Eq9-78-qhY">
<rect key="frame" x="8" y="26" width="250" height="30"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="send"/>
<connections>
<outlet property="delegate" destination="FVA-Ip-uIE" id="tUP-2R-seQ"/>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="wwz-tj-MLo">
<rect key="frame" x="8" y="3" width="250" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="WZS-UQ-rXy">
<rect key="frame" x="266" y="30" width="46" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="18"/>
<state key="normal" title="Send">
<color key="titleColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onSend:" destination="FVA-Ip-uIE" eventType="touchUpInside" id="0VT-Nd-fdz"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="tintColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<extendedEdge key="edgesForExtendedLayout" bottom="YES"/>
<connections>
<outlet property="loginPage" destination="FDy-KL-ozA" id="zBO-Gk-9OU"/>
<outlet property="messageArea" destination="yh5-ME-DKM" id="nFQ-Zd-Gmx"/>
<outlet property="messageField" destination="Eq9-78-qhY" id="HDE-p4-jGM"/>
<outlet property="nickName" destination="Xat-ec-Oga" id="xyE-A7-aZ5"/>
<outlet property="sendBtn" destination="WZS-UQ-rXy" id="nbZ-e0-RoK"/>
<outlet property="tableView" destination="Zd9-dA-z1N" id="Ekd-Is-nd5"/>
<outlet property="typingLabel" destination="wwz-tj-MLo" id="GxR-Bb-TGT"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="X4s-OP-QGg" userLabel="First Responder" sceneMemberID="firstResponder"/>
<view contentMode="scaleToFill" id="FDy-KL-ozA">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your Nickname:" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Vbn-qK-OOg">
<rect key="frame" x="40" y="200" width="240" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" id="Xat-ec-Oga">
<rect key="frame" x="40" y="269" width="240" height="30"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="16"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<outlet property="delegate" destination="FVA-Ip-uIE" id="fDd-Hi-o32"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
</objects>
<point key="canvasLocation" x="99" y="-451"/>
</scene>
</scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination"/>
</simulatedMetricsContainer>
</document>

View File

@@ -1,16 +0,0 @@
//
// main.m
// SioChatDemo
//
// Created by Melo Yao on 3/30/15.
// Copyright (c) 2015 Melo Yao. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.socketio.melode.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -1,40 +0,0 @@
//
// SioChatDemoTests.m
// SioChatDemoTests
//
// Created by Melo Yao on 3/30/15.
// Copyright (c) 2015 Melo Yao. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
@interface SioChatDemoTests : XCTestCase
@end
@implementation SioChatDemoTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, @"Pass");
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
@end

View File

@@ -1,4 +0,0 @@
ios/*
osx/*
src/*
boost_*

View File

@@ -1,378 +0,0 @@
#===============================================================================
# Filename: boost.sh
# Author: Pete Goodliffe
# Copyright: (c) Copyright 2009 Pete Goodliffe
# Licence: Please feel free to use this, with attribution
# Modified version
#===============================================================================
#
# Builds a Boost framework for the iPhone.
# Creates a set of universal libraries that can be used on an iPhone and in the
# iPhone simulator. Then creates a pseudo-framework to make using boost in Xcode
# less painful.
#
# To configure the script, define:
# BOOST_LIBS: which libraries to build
# IPHONE_SDKVERSION: iPhone SDK version (e.g. 5.1)
#
# Then go get the source tar.bz of the boost you want to build, shove it in the
# same directory as this script, and run "./boost.sh". Grab a cuppa. And voila.
#===============================================================================
: ${BOOST_LIBS:="random regex graph random chrono thread signals filesystem system date_time"}
: ${IPHONE_SDKVERSION:=`xcodebuild -showsdks | grep iphoneos | egrep "[[:digit:]]+\.[[:digit:]]+" -o | tail -1`}
: ${OSX_SDKVERSION:=10.8}
: ${XCODE_ROOT:=`xcode-select -print-path`}
: ${EXTRA_CPPFLAGS:="-DBOOST_AC_USE_PTHREADS -DBOOST_SP_USE_PTHREADS -std=c++11 -stdlib=libc++"}
# The EXTRA_CPPFLAGS definition works around a thread race issue in
# shared_ptr. I encountered this historically and have not verified that
# the fix is no longer required. Without using the posix thread primitives
# an invalid compare-and-swap ARM instruction (non-thread-safe) was used for the
# shared_ptr use count causing nasty and subtle bugs.
#
# Should perhaps also consider/use instead: -BOOST_SP_USE_PTHREADS
: ${TARBALLDIR:=`pwd`}
: ${SRCDIR:=`pwd`/src}
: ${IOSBUILDDIR:=`pwd`/ios/build}
: ${OSXBUILDDIR:=`pwd`/osx/build}
: ${PREFIXDIR:=`pwd`/ios/prefix}
: ${IOSFRAMEWORKDIR:=`pwd`/ios/framework}
: ${OSXFRAMEWORKDIR:=`pwd`/osx/framework}
: ${COMPILER:="clang++"}
: ${BOOST_VERSION:=1.55.0}
: ${BOOST_VERSION2:=1_55_0}
BOOST_TARBALL=$TARBALLDIR/boost_$BOOST_VERSION2.tar.bz2
BOOST_SRC=$SRCDIR/boost_${BOOST_VERSION2}
#===============================================================================
ARM_DEV_CMD="xcrun --sdk iphoneos"
SIM_DEV_CMD="xcrun --sdk iphonesimulator"
OSX_DEV_CMD="xcrun --sdk macosx"
ARM_COMBINED_LIB=$IOSBUILDDIR/lib_boost_arm.a
SIM_COMBINED_LIB=$IOSBUILDDIR/lib_boost_x86.a
#===============================================================================
#===============================================================================
# Functions
#===============================================================================
abort()
{
echo
echo "Aborted: $@"
exit 1
}
doneSection()
{
echo
echo "================================================================="
echo "Done"
echo
}
#===============================================================================
cleanEverythingReadyToStart()
{
echo Cleaning everything before we start to build...
rm -rf iphone-build iphonesim-build osx-build
rm -rf $IOSBUILDDIR
rm -rf $OSXBUILDDIR
rm -rf $PREFIXDIR
rm -rf $IOSFRAMEWORKDIR/$FRAMEWORK_NAME.framework
rm -rf $OSXFRAMEWORKDIR/$FRAMEWORK_NAME.framework
doneSection
}
#===============================================================================
downloadBoost()
{
if [ ! -s $TARBALLDIR/boost_${BOOST_VERSION2}.tar.bz2 ]; then
echo "Downloading boost ${BOOST_VERSION}"
curl -L -o $TARBALLDIR/boost_${BOOST_VERSION2}.tar.bz2 http://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION}/boost_${BOOST_VERSION2}.tar.bz2/download
fi
doneSection
}
#===============================================================================
unpackBoost()
{
[ -f "$BOOST_TARBALL" ] || abort "Source tarball missing."
echo Unpacking boost into $SRCDIR...
[ -d $SRCDIR ] || mkdir -p $SRCDIR
[ -d $BOOST_SRC ] || ( cd $SRCDIR; tar xfj $BOOST_TARBALL )
[ -d $BOOST_SRC ] && echo " ...unpacked as $BOOST_SRC"
doneSection
}
#===============================================================================
restoreBoost()
{
cp $BOOST_SRC/tools/build/v2/user-config.jam-bk $BOOST_SRC/tools/build/v2/user-config.jam
}
#===============================================================================
updateBoost()
{
echo Updating boost into $BOOST_SRC...
cp $BOOST_SRC/tools/build/v2/user-config.jam $BOOST_SRC/tools/build/v2/user-config.jam-bk
cat >> $BOOST_SRC/tools/build/v2/user-config.jam <<EOF
using darwin : ${IPHONE_SDKVERSION}~iphone
: $XCODE_ROOT/Toolchains/XcodeDefault.xctoolchain/usr/bin/$COMPILER -arch armv6 -arch armv7 -arch armv7s -arch arm64 -fvisibility=hidden -fvisibility-inlines-hidden $EXTRA_CPPFLAGS
: <striper> <root>$XCODE_ROOT/Platforms/iPhoneOS.platform/Developer
: <architecture>arm <target-os>iphone
;
using darwin : ${IPHONE_SDKVERSION}~iphonesim
: $XCODE_ROOT/Toolchains/XcodeDefault.xctoolchain/usr/bin/$COMPILER -arch i386 -arch x86_64 -fvisibility=hidden -fvisibility-inlines-hidden $EXTRA_CPPFLAGS
: <striper> <root>$XCODE_ROOT/Platforms/iPhoneSimulator.platform/Developer
: <architecture>x86 <target-os>iphone
;
EOF
doneSection
}
#===============================================================================
inventMissingHeaders()
{
# These files are missing in the ARM iPhoneOS SDK, but they are in the simulator.
# They are supported on the device, so we copy them from x86 SDK to a staging area
# to use them on ARM, too.
echo Invent missing headers
cp $XCODE_ROOT/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDKVERSION}.sdk/usr/include/{crt_externs,bzlib}.h $BOOST_SRC
}
#===============================================================================
bootstrapBoost()
{
cd $BOOST_SRC
BOOST_LIBS_COMMA=$(echo $BOOST_LIBS | sed -e "s/ /,/g")
echo "Bootstrapping (with libs $BOOST_LIBS_COMMA)"
./bootstrap.sh --with-libraries=$BOOST_LIBS_COMMA
doneSection
}
#===============================================================================
buildBoostForIPhoneOS()
{
cd $BOOST_SRC
# Install this one so we can copy the includes for the frameworks...
./bjam -j16 --build-dir=iphone-build --stagedir=iphone-build/stage --prefix=$PREFIXDIR toolset=darwin architecture=arm target-os=iphone macosx-version=iphone-${IPHONE_SDKVERSION} define=_LITTLE_ENDIAN link=static stage
./bjam -j16 --build-dir=iphone-build --stagedir=iphone-build/stage --prefix=$PREFIXDIR toolset=darwin architecture=arm target-os=iphone macosx-version=iphone-${IPHONE_SDKVERSION} define=_LITTLE_ENDIAN link=static install
doneSection
./bjam -j16 --build-dir=iphonesim-build --stagedir=iphonesim-build/stage --toolset=darwin-${IPHONE_SDKVERSION}~iphonesim architecture=x86 target-os=iphone macosx-version=iphonesim-${IPHONE_SDKVERSION} link=static stage
doneSection
# ./b2 -j16 --build-dir=osx-build --stagedir=osx-build/stage toolset=clang cxxflags="-std=c++11 -stdlib=libc++ -arch i386 -arch x86_64" linkflags="-stdlib=libc++" link=static threading=multi stage
doneSection
}
#===============================================================================
scrunchAllLibsTogetherInOneLibPerPlatform()
{
cd $BOOST_SRC
mkdir -p $IOSBUILDDIR/armv6/obj
mkdir -p $IOSBUILDDIR/armv7/obj
mkdir -p $IOSBUILDDIR/armv7s/obj
mkdir -p $IOSBUILDDIR/arm64/obj
mkdir -p $IOSBUILDDIR/i386/obj
mkdir -p $IOSBUILDDIR/x86_64/obj
mkdir -p $OSXBUILDDIR/i386/obj
mkdir -p $OSXBUILDDIR/x86_64/obj
ALL_LIBS=""
echo Splitting all existing fat binaries...
for NAME in $BOOST_LIBS; do
ALL_LIBS="$ALL_LIBS libboost_$NAME.a"
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv6 -o $IOSBUILDDIR/armv6/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv7 -o $IOSBUILDDIR/armv7/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv7s -o $IOSBUILDDIR/armv7s/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin arm64 -o $IOSBUILDDIR/arm64/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphonesim-build/stage/lib/libboost_$NAME.a" -thin i386 -o $IOSBUILDDIR/i386/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphonesim-build/stage/lib/libboost_$NAME.a" -thin x86_64 -o $IOSBUILDDIR/x86_64/libboost_$NAME.a
$ARM_DEV_CMD lipo "osx-build/stage/lib/libboost_$NAME.a" -thin i386 -o $OSXBUILDDIR/i386/libboost_$NAME.a
$ARM_DEV_CMD lipo "osx-build/stage/lib/libboost_$NAME.a" -thin x86_64 -o $OSXBUILDDIR/x86_64/libboost_$NAME.a
done
echo "Decomposing each architecture's .a files"
for NAME in $ALL_LIBS; do
echo Decomposing $NAME...
(cd $IOSBUILDDIR/armv6/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/armv7/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/armv7s/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/arm64/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/i386/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/x86_64/obj; ar -x ../$NAME );
(cd $OSXBUILDDIR/i386/obj; ar -x ../$NAME );
(cd $OSXBUILDDIR/x86_64/obj; ar -x ../$NAME );
done
echo "Linking each architecture into an uberlib ($ALL_LIBS => libboost.a )"
rm $IOSBUILDDIR/*/libboost.a
echo ...armv6
(cd $IOSBUILDDIR/armv6; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...armv7
(cd $IOSBUILDDIR/armv7; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...armv7s
(cd $IOSBUILDDIR/armv7s; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...arm64
(cd $IOSBUILDDIR/arm64; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...i386
(cd $IOSBUILDDIR/i386; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...x86_64
(cd $IOSBUILDDIR/x86_64; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
rm $OSXBUILDDIR/*/libboost.a
echo ...osx-i386
(cd $OSXBUILDDIR/i386; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...x86_64
(cd $OSXBUILDDIR/x86_64; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
}
#===============================================================================
buildFramework()
{
: ${1:?}
FRAMEWORKDIR=$1
BUILDDIR=$2
VERSION_TYPE=Alpha
FRAMEWORK_NAME=boost
FRAMEWORK_VERSION=A
FRAMEWORK_CURRENT_VERSION=$BOOST_VERSION
FRAMEWORK_COMPATIBILITY_VERSION=$BOOST_VERSION
FRAMEWORK_BUNDLE=$FRAMEWORKDIR/$FRAMEWORK_NAME.framework
echo "Framework: Building $FRAMEWORK_BUNDLE from $BUILDDIR..."
rm -rf $FRAMEWORK_BUNDLE
echo "Framework: Setting up directories..."
mkdir -p $FRAMEWORK_BUNDLE
mkdir -p $FRAMEWORK_BUNDLE/Versions
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Resources
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Headers
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Documentation
echo "Framework: Creating symlinks..."
ln -s $FRAMEWORK_VERSION $FRAMEWORK_BUNDLE/Versions/Current
ln -s Versions/Current/Headers $FRAMEWORK_BUNDLE/Headers
ln -s Versions/Current/Resources $FRAMEWORK_BUNDLE/Resources
ln -s Versions/Current/Documentation $FRAMEWORK_BUNDLE/Documentation
ln -s Versions/Current/$FRAMEWORK_NAME $FRAMEWORK_BUNDLE/$FRAMEWORK_NAME
FRAMEWORK_INSTALL_NAME=$FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/$FRAMEWORK_NAME
echo "Lipoing library into $FRAMEWORK_INSTALL_NAME..."
$ARM_DEV_CMD lipo -create $BUILDDIR/*/libboost.a -o "$FRAMEWORK_INSTALL_NAME" || abort "Lipo $1 failed"
echo "Framework: Copying includes..."
cp -r $PREFIXDIR/include/boost/* $FRAMEWORK_BUNDLE/Headers/
echo "Framework: Creating plist..."
cat > $FRAMEWORK_BUNDLE/Resources/Info.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${FRAMEWORK_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.boost</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${FRAMEWORK_CURRENT_VERSION}</string>
</dict>
</plist>
EOF
doneSection
}
#===============================================================================
# Execution starts here
#===============================================================================
mkdir -p $IOSBUILDDIR
cleanEverythingReadyToStart #may want to comment if repeatedly running during dev
restoreBoost
echo "BOOST_VERSION: $BOOST_VERSION"
echo "BOOST_LIBS: $BOOST_LIBS"
echo "BOOST_SRC: $BOOST_SRC"
echo "IOSBUILDDIR: $IOSBUILDDIR"
echo "OSXBUILDDIR: $OSXBUILDDIR"
echo "PREFIXDIR: $PREFIXDIR"
echo "IOSFRAMEWORKDIR: $IOSFRAMEWORKDIR"
echo "OSXFRAMEWORKDIR: $OSXFRAMEWORKDIR"
echo "IPHONE_SDKVERSION: $IPHONE_SDKVERSION"
echo "XCODE_ROOT: $XCODE_ROOT"
echo "COMPILER: $COMPILER"
echo
downloadBoost
unpackBoost
inventMissingHeaders
bootstrapBoost
updateBoost
buildBoostForIPhoneOS
scrunchAllLibsTogetherInOneLibPerPlatform
buildFramework $IOSFRAMEWORKDIR $IOSBUILDDIR
buildFramework $OSXFRAMEWORKDIR $OSXBUILDDIR
restoreBoost
echo "Completed successfully"
#===============================================================================

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.socketio.melode.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -1,118 +0,0 @@
version: "{branch} (#{build})"
image:
- Visual Studio 2015
- Visual Studio 2017
- Visual Studio 2019
environment:
DEBUG: 1
WARNINGS: 1
matrix:
- STANDALONE: 1
HEADER_ONLY: 1
MSVC: 1
- STANDALONE: 1
SEPARATE_COMPILATION: 1
MSVC: 1
- STANDALONE: 1
MINGW: 1
- STANDALONE: 1
CXXLATEST: 1
MSVC: 1
- STANDALONE: 1
HEADER_ONLY: 1
WIN9X: 1
MSVC: 1
- STANDALONE: 1
SEPARATE_COMPILATION: 1
WIN9X: 1
MSVC: 1
- USING_BOOST: 1
HEADER_ONLY: 1
MSVC: 1
- USING_BOOST: 1
SEPARATE_COMPILATION: 1
MSVC: 1
- USING_BOOST: 1
MINGW: 1
for:
-
matrix:
only:
- image: Visual Studio 2015
MSVC: 1
environment:
BOOSTDIR: C:\Libraries\boost_1_67_0
build_script:
- call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64
- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64
- cd asio\src
- nmake -f Makefile.msc
- nmake -f Makefile.msc check
-
matrix:
only:
- image: Visual Studio 2017
MSVC: 1
environment:
BOOSTDIR: C:\Libraries\boost_1_69_0
_WIN32_WINNT: 0x0603
build_script:
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64
- cd asio\src
- nmake -f Makefile.msc
- nmake -f Makefile.msc check
-
matrix:
only:
- image: Visual Studio 2019
MSVC: 1
environment:
BOOSTDIR: C:\Libraries\boost_1_83_0
_WIN32_WINNT: 0x0A00
build_script:
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64
- cd asio\src
- nmake -f Makefile.msc
- nmake -f Makefile.msc check
-
matrix:
only:
- image: Visual Studio 2019
MINGW: 1
environment:
BOOSTDIR: C:/Libraries/boost_1_83_0
build_script:
- PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;C:\msys64\usr\bin;%PATH%
- cd asio\src
- mingw32-make -f Makefile.mgw
- mingw32-make -f Makefile.mgw check
matrix:
exclude:
- image: Visual Studio 2015
HEADER_ONLY: 1
- image: Visual Studio 2015
CXXLATEST: 1
- image: Visual Studio 2015
WIN9X: 1
- image: Visual Studio 2015
USING_BOOST: 1
- image: Visual Studio 2015
MINGW: 1
- image: Visual Studio 2017
SEPARATE_COMPILATION: 1
- image: Visual Studio 2017
CXXLATEST: 1
- image: Visual Studio 2017
WIN9X: 1
- image: Visual Studio 2017
USING_BOOST: 1
- image: Visual Studio 2017
MINGW: 1
- image: Visual Studio 2019
HEADER_ONLY: 1
- image: Visual Studio 2019
WIN9X: 1

View File

@@ -1,16 +0,0 @@
freebsd_instance:
image_family: freebsd-14-0
cpu: 1
env:
CXXFLAGS: -std=c++14 -Wall -Wextra -O2
task:
install_script:
- pkg install -y autoconf automake pkgconf
build_script:
- cd asio
- ./autogen.sh
- ./configure --with-boost=no
- make
- make check

View File

@@ -1,343 +0,0 @@
name: asio CI
on:
push:
branches: [ master, citest-* ]
jobs:
build:
strategy:
fail-fast: false
matrix:
build-type: ['sanity']
runs-on: [ubuntu-20.04, ubuntu-22.04, macos-latest]
compiler: [g++-8, g++-9, g++-10, g++-12, clang++-10, clang++-14, g++]
cxx-std: ['c++11', 'c++14', 'c++17', 'c++2a', 'c++20']
separate-compilation: ['', '--enable-separate-compilation']
optim-level: ['-O0']
no-deprecated: ['']
select-reactor: ['', '-DASIO_DISABLE_EPOLL', '-DASIO_DISABLE_KQUEUE']
handler-tracking: ['']
boost: ['']
boost-url: ['']
exclude:
# New compilers don't run on ubuntu 20.04
- runs-on: ubuntu-20.04
compiler: g++-12
- runs-on: ubuntu-20.04
compiler: clang++-14
# Older compilers don't run on ubuntu 22.04
- runs-on: ubuntu-22.04
compiler: g++-8
- runs-on: ubuntu-22.04
compiler: clang++-10
# Unversioned g++ doesn't run on ubuntu
- runs-on: ubuntu-20.04
compiler: g++
- runs-on: ubuntu-22.04
compiler: g++
# Versioned g++ and clang++ don't run on macOS
- runs-on: macos-latest
compiler: g++-8
- runs-on: macos-latest
compiler: g++-9
- runs-on: macos-latest
compiler: g++-10
- runs-on: macos-latest
compiler: g++-12
- runs-on: macos-latest
compiler: clang++-10
- runs-on: macos-latest
compiler: clang++-14
# Older compilers don't support newer std variants
- compiler: g++-8
cxx-std: c++20
- compiler: g++-9
cxx-std: c++20
- compiler: g++-10
cxx-std: c++2a
- compiler: g++-12
cxx-std: c++2a
- compiler: clang++-10
cxx-std: c++20
- compiler: clang++-14
cxx-std: c++20
- compiler: clang++-14
cxx-std: c++2a
- runs-on: macos-latest
cxx-std: c++20
# Specifying the select reactor is OS-specific
- runs-on: ubuntu-20.04
select-reactor: -DASIO_DISABLE_KQUEUE
- runs-on: ubuntu-22.04
select-reactor: -DASIO_DISABLE_KQUEUE
- runs-on: macos-latest
select-reactor: -DASIO_DISABLE_EPOLL
# Trim builds that use separate compilation
- compiler: g++-8
separate-compilation: --enable-separate-compilation
- compiler: g++-9
separate-compilation: --enable-separate-compilation
- compiler: g++-10
separate-compilation: --enable-separate-compilation
- runs-on: macos-latest
cxx-std: c++14
separate-compilation: --enable-separate-compilation
- runs-on: macos-latest
cxx-std: c++17
separate-compilation: --enable-separate-compilation
# Trim builds that use select reactor
- compiler: g++-8
select-reactor: -DASIO_DISABLE_EPOLL
- compiler: g++-9
select-reactor: -DASIO_DISABLE_EPOLL
- compiler: g++-10
select-reactor: -DASIO_DISABLE_EPOLL
include:
#
# Linux / g++-12 -std=c++20 -fcoroutines / -O2 / standalone
#
- build-type: full
runs-on: ubuntu-22.04
compiler: g++-12
cxx-std: c++20 -fcoroutines
optim-level: -O2
#
# Linux / g++-12 -std=c++17 / -O2 / boost 1.76
#
- build-type: full
runs-on: ubuntu-22.04
compiler: g++-12
cxx-std: c++17
optim-level: -O2
with-boost: --with-boost=$GITHUB_WORKSPACE/boost_1_76_0
boost-url: https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2
#
# Linux / g++-10 / -O2 / standalone
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
optim-level: -O2
#
# Linux / g++-10 / -O0 / standalone / handler tracking
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
optim-level: -O0
handler-tracking: -DASIO_ENABLE_HANDLER_TRACKING
#
# Linux / g++-10 / -O0 / standalone / epoll disabled
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
optim-level: -O0
select-reactor: -DASIO_DISABLE_EPOLL
#
# Linux / g++-10 / -O0 / standalone / separate compilation / handler tracking
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
separate-compilation: --enable-separate-compilation
optim-level: -O0
handler-tracking: -DASIO_ENABLE_HANDLER_TRACKING
#
# Linux / g++-10 / -O0 / standalone / separate compilation / epoll disabled
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
separate-compilation: --enable-separate-compilation
optim-level: -O0
select-reactor: -DASIO_DISABLE_EPOLL
#
# Linux / g++-10 / -O2 / boost 1.83
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
optim-level: -O2
with-boost: --with-boost=$GITHUB_WORKSPACE/boost_1_83_0
boost-url: https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2
#
# Linux / g++-10 / -O0 / boost 1.83 / epoll disabled
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
optim-level: -O0
with-boost: --with-boost=$GITHUB_WORKSPACE/boost_1_83_0
boost-url: https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2
select-reactor: -DASIO_DISABLE_EPOLL
#
# Linux / g++-10 / -O0 / boost 1.83 / separate compilation
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-10
cxx-std: c++14
separate-compilation: --enable-separate-compilation
optim-level: -O0
with-boost: --with-boost=$GITHUB_WORKSPACE/boost_1_83_0
boost-url: https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2
#
# Linux / g++-8 / -O2 / standalone
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-8
cxx-std: c++11
optim-level: -O2
#
# Linux / g++-8 / -O0 / standalone / separate compilation
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-8
cxx-std: c++11
separate-compilation: --enable-separate-compilation
optim-level: -O0
#
# Linux / g++-8 -std=c++11 / -O2 / boost 1.83
#
- build-type: full
runs-on: ubuntu-20.04
compiler: g++-8
cxx-std: c++11
separate-compilation: --enable-separate-compilation
optim-level: -O2
with-boost: --with-boost=$GITHUB_WORKSPACE/boost_1_83_0
boost-url: https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2
#
# Linux / clang++-14 -std=c++2a / -O2 / standalone
#
- build-type: full
runs-on: ubuntu-22.04
compiler: clang++-14
cxx-std: c++2a
cxx-stdlib: -stdlib=libc++
optim-level: -O2
#
# Linux / clang++-14 -std=c++11 / -O0 / standalone / separate compilation
#
- build-type: full
runs-on: ubuntu-22.04
compiler: clang++-14
cxx-std: c++11
separate-compilation: --enable-separate-compilation
optim-level: -O0
#
# Linux / clang++-14 -std=c++20 / -O2 / standalone / separate compilation
#
- build-type: full
runs-on: ubuntu-22.04
compiler: clang++-14
cxx-std: c++20
cxx-stdlib: -stdlib=libc++
separate-compilation: --enable-separate-compilation
optim-level: -O2
#
# Linux / clang++-10 -std=c++11 / -O2 / standalone
#
- build-type: full
runs-on: ubuntu-20.04
compiler: clang++-10
cxx-std: c++11
optim-level: -O2
#
# macOS / c++2a -fcoroutines-ts / -O2 / standalone
#
- build-type: full
runs-on: macos-latest
compiler: g++
cxx-std: c++2a -fcoroutines-ts
optim-level: -O2
#
# macOS / c++11 / -O2 / standalone
#
- build-type: full
runs-on: macos-latest
compiler: g++
cxx-std: c++11
optim-level: -O2
#
# macOS / c++11 / -O0 / standalone / kqueue disabled
#
- build-type: full
runs-on: macos-latest
compiler: g++
cxx-std: c++11
optim-level: -O0
select-reactor: -DASIO_DISABLE_KQUEUE
#
# macOS / c++11 / -O0 / standalone / separate compilation
#
- build-type: full
runs-on: macos-latest
compiler: g++
cxx-std: c++11
separate-compilation: --enable-separate-compilation
optim-level: -O0
#
# macOS / c++11 / -O2 / boost 1.83
#
- build-type: full
runs-on: macos-latest
compiler: g++
cxx-std: c++11
optim-level: -O2
with-boost: --with-boost=$GITHUB_WORKSPACE/boost_1_83_0
boost-url: https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2
#
# macOS / c++11 / -O2 / boost 1.83 / separate compilation
#
- build-type: full
runs-on: macos-latest
compiler: g++
cxx-std: c++11
separate-compilation: --enable-separate-compilation
optim-level: -O0
with-boost: --with-boost=$GITHUB_WORKSPACE/boost_1_83_0
boost-url: https://boostorg.jfrog.io/artifactory/main/release/1.83.0/source/boost_1_83_0.tar.bz2
runs-on: ${{ matrix.runs-on }}
env:
CXX: ${{ matrix.compiler }}
CXXFLAGS: -std=${{ matrix.cxx-std }} ${{ matrix.cxx-stdlib }} ${{ matrix.optim-level }} -Wall -Wextra ${{ matrix.no-deprecated }} ${{ matrix.select-reactor }} ${{ matrix.handler-tracking }}
steps:
- uses: actions/checkout@v4
- name: Install autotools
if: startsWith(matrix.runs-on, 'macos')
run: brew install automake
- name: Install compiler
if: startsWith(matrix.runs-on, 'ubuntu')
run: sudo apt-get install -y ${{ matrix.compiler }}
- name: Install boost
if: startsWith(matrix.with-boost, '--with-boost=$GITHUB_WORKSPACE')
run: |
wget --quiet -O - ${{ matrix.boost-url }} | tar -xj
- name: Configure
working-directory: asio
run: |
./autogen.sh
./configure ${{ matrix.separate-compilation }} ${{ matrix.with-boost }}
- name: Line length check
working-directory: asio
run: perl ./boostify.pl --includes-only
- name: Sanity check
if: startsWith(matrix.build-type, 'sanity')
working-directory: asio/src/tests
run: make unit/io_context.log unit/ip/tcp.log unit/ts/net.log
- name: Build
if: startsWith(matrix.build-type, 'full')
working-directory: asio
run: make && make check

View File

@@ -1,3 +0,0 @@
/*.cpp
/*.hpp
/boost

View File

@@ -1,24 +0,0 @@
Makefile
Makefile.in
aclocal.m4
asio.pc
autom4te.cache
compile
config.guess
config.log
config.status
config.sub
configure
depcomp
install-sh
missing
test-driver
/doc
/lib
/boostified
/tsified
*.gz
*.bz2
*.zip
/*.cpp
/*.hpp

View File

@@ -1,4 +0,0 @@
Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@@ -1,5 +0,0 @@
See doc/index.html for information on:
- External dependencies
- Using, building, and configuring Asio
- Supported platforms
- How to build the tests and examples

View File

@@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -1,22 +0,0 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 dist-zip
pkgconfig_DATA = asio.pc
pkgconfigdir = $(libdir)/pkgconfig
SUBDIRS = include src
MAINTAINERCLEANFILES = \
$(srcdir)/aclocal.m4 \
$(srcdir)/configure \
$(srcdir)/config.guess \
$(srcdir)/config.sub \
$(srcdir)/depcomp \
$(srcdir)/install-sh \
$(srcdir)/missing \
$(srcdir)/mkinstalldirs \
$(srcdir)/Makefile.in \
asio-*.tar.gz
EXTRA_DIST = \
LICENSE_1_0.txt \
doc

View File

@@ -1,4 +0,0 @@
asio version 1.31.0
Released Monday, 05 August 2024.
See doc/index.html for API documentation and a tutorial.

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
includedir=@includedir@
Name: @PACKAGE_NAME@
Description: A cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
Lflags:
Requires:
Requires.private:

View File

@@ -1,55 +0,0 @@
#!/bin/sh
# Helps bootstrapping the application when checked out from CVS.
# Requires GNU autoconf, GNU automake and GNU which.
#
# Copyright (C) 2004, by
#
# Carlo Wood, Run on IRC <carlo@alinoe.com>
# RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt
# Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
#
# Do sanity checks.
# Directory check.
if [ ! -f autogen.sh ]; then
echo "Run ./autogen.sh from the directory it exists in."
exit 1
fi
AUTOMAKE=${AUTOMAKE:-automake}
ACLOCAL=${ACLOCAL:-aclocal}
AUTOCONF=${AUTOCONF:-autoconf}
($AUTOCONF --version) >/dev/null 2>/dev/null || (echo "You need GNU autoconf to install from CVS (ftp://ftp.gnu.org/gnu/autoconf/)"; exit 1) || exit 1
($AUTOMAKE --version) >/dev/null 2>/dev/null || (echo "You need GNU automake 1.7 or higher to install from CVS (ftp://ftp.gnu.org/gnu/automake/)"; exit 1) || exit 1
# Determine the version of automake.
automake_version=`$AUTOMAKE --version | head -n 1 | sed -e 's/[^12]*\([12]\.[0-9][^ ]*\).*/\1/'`
automake_major=`echo $automake_version | cut -f1 -d.`
automake_minor=`echo $automake_version | cut -f2 -d.`
automake_version_number=`expr "$automake_major" \* 1000 \+ "$automake_minor"`
# Require automake 1.7.
if expr "1007" \> "$automake_version_number" >/dev/null; then
$AUTOMAKE --version | head -n 1
echo ""
echo "Fatal error: automake 1.7 or higher is required. Please set \$AUTOMAKE"
echo "to point to a newer automake, or upgrade."
echo ""
exit 1
fi
run()
{
echo "Running $1 ..."
$1
}
# This is needed when someone just upgraded automake and this cache is still generated by an old version.
rm -rf autom4te.cache config.cache
run "$ACLOCAL"
run "$AUTOCONF"
run "$AUTOMAKE --add-missing --foreign"

File diff suppressed because it is too large Load Diff

View File

@@ -1,700 +0,0 @@
#!/usr/bin/perl -w
use strict;
use File::Path;
our $boost_dir = "boostified";
our $bad_lines = 0;
sub print_line
{
my ($output, $line, $from, $lineno) = @_;
# Warn if the resulting line is >80 characters wide.
if (length($line) > 80)
{
if ($from =~ /\.[chi]pp$/)
{
++$bad_lines;
print("Warning: $from:$lineno: output >80 characters wide.\n");
}
}
# Write the output.
print($output $line . "\n");
}
sub source_contains_asio_thread_usage
{
my ($from) = @_;
# Open the input file.
open(my $input, "<$from") or die("Can't open $from for reading");
# Check file for use of asio::thread.
while (my $line = <$input>)
{
chomp($line);
if ($line =~ /asio::thread/)
{
close($input);
return 1;
}
elsif ($line =~ /^ *thread /)
{
close($input);
return 1;
}
}
close($input);
return 0;
}
sub source_contains_asio_include
{
my ($from) = @_;
# Open the input file.
open(my $input, "<$from") or die("Can't open $from for reading");
# Check file for inclusion of asio.hpp.
while (my $line = <$input>)
{
chomp($line);
if ($line =~ /# *include [<"]asio\.hpp[>"]/)
{
close($input);
return 1;
}
}
close($input);
return 0;
}
sub copy_source_file
{
my ($from, $to) = @_;
# Ensure the output directory exists.
my $dir = $to;
$dir =~ s/[^\/]*$//;
mkpath($dir);
# First determine whether the file makes any use of asio::thread.
my $uses_asio_thread = source_contains_asio_thread_usage($from);
my $includes_asio = source_contains_asio_include($from);
my $is_asio_hpp = 0;
$is_asio_hpp = 1 if ($from =~ /asio\.hpp/);
my $needs_doc_link = 0;
$needs_doc_link = 1 if ($is_asio_hpp);
my $is_error_hpp = 0;
$is_error_hpp = 1 if ($from =~ /asio\/error\.hpp/);
my $is_qbk = 0;
$is_qbk = 1 if ($from =~ /.qbk$/);
my $is_xsl = 0;
$is_xsl = 1 if ($from =~ /.xsl$/);
my $is_quickref = 0;
$is_quickref = 1 if ($from =~ /quickref.xml$/);
my $is_test = 0;
$is_test = 1 if ($from =~ /tests\/unit/);
my $is_coroutine_related = 0;
$is_coroutine_related = 1 if ($from =~ /await/ || $from =~ /partial_promise/ || $from =~ /co_composed/);
my $is_hash_related = 0;
$is_hash_related = 1 if ($from =~ /ip\/address/ || $from =~ /ip\/basic_endpoint/);
# Open the files.
open(my $input, "<$from") or die("Can't open $from for reading");
open(my $output, ">$to") or die("Can't open $to for writing");
# Copy the content.
my $lineno = 1;
while (my $line = <$input>)
{
chomp($line);
# Unconditional replacements.
$line =~ s/[\\@]ref boost_bind/boost::bind()/g;
if ($from =~ /.*\.txt$/)
{
$line =~ s/[\\@]ref async_read/boost::asio::async_read()/g;
$line =~ s/[\\@]ref async_write/boost::asio::async_write()/g;
}
if ($line =~ /asio_detail_posix_thread_function/)
{
$line =~ s/asio_detail_posix_thread_function/boost_asio_detail_posix_thread_function/g;
}
if ($line =~ /asio_signal_handler/)
{
$line =~ s/asio_signal_handler/boost_asio_signal_handler/g;
}
if ($line =~ /ASIO_/ && !($line =~ /BOOST_ASIO_/))
{
$line =~ s/ASIO_/BOOST_ASIO_/g;
}
# Extra replacements for quickbook, XSL and quickref.xml source only.
if ($is_qbk || $is_xsl || $is_quickref)
{
$line =~ s/asio\.examples/boost_asio.examples/g;
$line =~ s/asio\.history/boost_asio.history/g;
$line =~ s/asio\.index/boost_asio.index/g;
$line =~ s/asio\.net_ts/boost_asio.net_ts/g;
$line =~ s/asio\.std_executors/boost_asio.std_executors/g;
$line =~ s/asio\.overview/boost_asio.overview/g;
$line =~ s/asio\.reference/boost_asio.reference/g;
$line =~ s/asio\.tutorial/boost_asio.tutorial/g;
$line =~ s/asio\.using/boost_asio.using/g;
$line =~ s/Asio/Boost.Asio/g;
$line =~ s/changes made in each release/changes made in each Boost release/g;
$line =~ s/\[\$/[\$boost_asio\//g;
$line =~ s/\[@\.\.\/src\/examples/[\@boost_asio\/example/g;
$line =~ s/asio\//boost\/asio\//g if $is_xsl;
$line =~ s/include\/asio/boost\/asio/g;
$line =~ s/\^asio/^boost\/asio/g;
$line =~ s/namespaceasio/namespaceboost_1_1asio/g;
$line =~ s/ \(\[\@examples\/diffs.*$//;
$line =~ s/boost\/tools\/boostbook/tools\/boostbook/g;
}
# Conditional replacements.
if ($line =~ /^( *)namespace asio \{/)
{
if ($is_qbk)
{
print_line($output, $1 . "namespace boost { namespace asio {", $from, $lineno);
}
else
{
print_line($output, $1 . "namespace boost {", $from, $lineno);
print_line($output, $line, $from, $lineno);
}
}
elsif ($line =~ /^( *)} \/\/ namespace asio$/)
{
if ($is_qbk)
{
print_line($output, $1 . "} } // namespace boost::asio", $from, $lineno);
}
else
{
print_line($output, $line, $from, $lineno);
print_line($output, $1 . "} // namespace boost", $from, $lineno);
}
}
elsif ($line =~ /^(# *include )[<"](asio\.hpp)[>"]$/)
{
print_line($output, $1 . "<boost/" . $2 . ">", $from, $lineno);
if ($uses_asio_thread)
{
print_line($output, $1 . "<boost/thread/thread.hpp>", $from, $lineno) if (!$is_test);
$uses_asio_thread = 0;
}
}
elsif ($line =~ /^(# *include )[<"]boost\/.*[>"].*$/)
{
if (!$includes_asio && $uses_asio_thread)
{
print_line($output, $1 . "<boost/thread/thread.hpp>", $from, $lineno) if (!$is_test);
$uses_asio_thread = 0;
}
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /^(# *include )[<"]asio\/thread\.hpp[>"]/)
{
if ($is_test)
{
print_line($output, $1 . "<boost/asio/detail/thread.hpp>", $from, $lineno);
}
else
{
# Line is removed.
}
}
elsif ($line =~ /(# *include )[<"]asio\/error_code\.hpp[>"]/)
{
if ($is_asio_hpp)
{
# Line is removed.
}
else
{
print_line($output, $1 . "<boost/cerrno.hpp>", $from, $lineno) if ($is_error_hpp);
print_line($output, $1 . "<boost/system/error_code.hpp>", $from, $lineno);
}
}
elsif ($line =~ /# *include [<"]asio\/impl\/error_code\.[hi]pp[>"]/)
{
# Line is removed.
}
elsif ($line =~ /(# *include )[<"]asio\/system_error\.hpp[>"]/)
{
if ($is_asio_hpp)
{
# Line is removed.
}
else
{
print_line($output, $1 . "<boost/system/system_error.hpp>", $from, $lineno);
}
}
elsif ($line =~ /(^.*# *include )[<"](asio\/[^>"]*)[>"](.*)$/)
{
print_line($output, $1 . "<boost/" . $2 . ">" . $3, $from, $lineno);
}
elsif ($line =~ /#.*defined\(.*ASIO_HAS_STD_SYSTEM_ERROR\)$/)
{
# Line is removed.
}
elsif ($line =~ /asio::thread\b/)
{
if ($is_test)
{
$line =~ s/asio::thread/asio::detail::thread/g;
}
else
{
$line =~ s/asio::thread/boost::thread/g;
}
if (!($line =~ /boost::asio::/))
{
$line =~ s/asio::/boost::asio::/g;
}
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /^( *)thread( .*)$/ && !$is_qbk)
{
if ($is_test)
{
print_line($output, $1 . "boost::asio::detail::thread" . $2, $from, $lineno);
}
else
{
print_line($output, $1 . "boost::thread" . $2, $from, $lineno);
}
}
elsif ($line =~ /namespace std \{ *$/ && !$is_coroutine_related && !$is_hash_related)
{
print_line($output, "namespace boost {", $from, $lineno);
print_line($output, "namespace system {", $from, $lineno);
}
elsif ($line =~ /std::error_code/)
{
$line =~ s/std::error_code/boost::system::error_code/g;
$line =~ s/asio::/boost::asio::/g if !$is_xsl;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /std::system_error/)
{
$line =~ s/std::system_error/boost::system::system_error/g;
$line =~ s/asio::/boost::asio::/g if !$is_xsl;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /ec\.assign\(0, ec\.category\(\)\)/)
{
$line =~ s/ec\.assign\(0, ec\.category\(\)\)/ec = boost::system::error_code()/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /^} \/\/ namespace std/ && !$is_coroutine_related && !$is_hash_related)
{
print_line($output, "} // namespace system", $from, $lineno);
print_line($output, "} // namespace boost", $from, $lineno);
}
elsif ($line =~ /asio::/ && !($line =~ /boost::asio::/))
{
$line =~ s/asio::error_code/boost::system::error_code/g;
$line =~ s/asio::error_category/boost::system::error_category/g;
$line =~ s/asio::system_category/boost::system::system_category/g;
$line =~ s/asio::system_error/boost::system::system_error/g;
$line =~ s/asio::/boost::asio::/g if !$is_xsl;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /using namespace asio/)
{
$line =~ s/using namespace asio/using namespace boost::asio/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /asio_handler_alloc_helpers/)
{
$line =~ s/asio_handler_alloc_helpers/boost_asio_handler_alloc_helpers/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /asio_handler_cont_helpers/)
{
$line =~ s/asio_handler_cont_helpers/boost_asio_handler_cont_helpers/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /asio_handler_invoke_helpers/)
{
$line =~ s/asio_handler_invoke_helpers/boost_asio_handler_invoke_helpers/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /asio_(prefer|query|require|require_concept)_fn/)
{
$line =~ s/asio_(prefer|query|require|require_concept)_fn/boost_asio_$1_fn/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /asio_execution_/ && !($line =~ /_is_unspecialised/))
{
$line =~ s/asio_execution_/boost_asio_execution_/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /[\\@]ref boost_bind/)
{
$line =~ s/[\\@]ref boost_bind/boost::bind()/g;
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /( *)\[category template\]/)
{
print_line($output, $1 . "[authors [Kohlhoff, Christopher]]", $from, $lineno);
print_line($output, $line, $from, $lineno);
}
elsif ($line =~ /boostify: non-boost docs start here/)
{
while ($line = <$input>)
{
last if $line =~ /boostify: non-boost docs end here/;
}
}
elsif ($line =~ /boostify: non-boost code starts here/)
{
while ($line = <$input>)
{
last if $line =~ /boostify: non-boost code ends here/;
}
}
elsif ($line =~ /^$/ && $needs_doc_link)
{
$needs_doc_link = 0;
print_line($output, "// See www.boost.org/libs/asio for documentation.", $from, $lineno);
print_line($output, "//", $from, $lineno);
print_line($output, $line, $from, $lineno);
}
elsif ($is_quickref)
{
if ($line =~ /asio\.reference\.error_code">/)
{
# Line is removed.
}
elsif ($line =~ /asio\.reference\.system_error">/)
{
# Line is removed.
}
elsif ($line =~ /asio\.reference\.thread">/)
{
# Line is removed.
}
else
{
print_line($output, $line, $from, $lineno);
}
}
else
{
print_line($output, $line, $from, $lineno);
}
++$lineno;
}
# Ok, we're done.
close($input);
close($output);
}
sub copy_include_files
{
my @dirs = (
"include",
"include/asio",
"include/asio/detail",
"include/asio/detail/impl",
"include/asio/execution",
"include/asio/execution/detail",
"include/asio/execution/impl",
"include/asio/experimental",
"include/asio/experimental/detail",
"include/asio/experimental/detail/impl",
"include/asio/experimental/impl",
"include/asio/generic",
"include/asio/generic/detail",
"include/asio/generic/detail/impl",
"include/asio/impl",
"include/asio/ip",
"include/asio/ip/impl",
"include/asio/ip/detail",
"include/asio/ip/detail/impl",
"include/asio/local",
"include/asio/local/detail",
"include/asio/local/detail/impl",
"include/asio/posix",
"include/asio/ssl",
"include/asio/ssl/detail",
"include/asio/ssl/detail/impl",
"include/asio/ssl/impl",
"include/asio/ssl/old",
"include/asio/ssl/old/detail",
"include/asio/traits",
"include/asio/ts",
"include/asio/windows");
foreach my $dir (@dirs)
{
our $boost_dir;
my @files = ( glob("$dir/*.hpp"), glob("$dir/*.ipp") );
foreach my $file (@files)
{
if ($file ne "include/asio/thread.hpp"
and $file ne "include/asio/error_code.hpp"
and $file ne "include/asio/system_error.hpp"
and $file ne "include/asio/impl/error_code.hpp"
and $file ne "include/asio/impl/error_code.ipp")
{
my $from = $file;
my $to = $file;
$to =~ s/^include\//$boost_dir\/libs\/asio\/include\/boost\//;
copy_source_file($from, $to);
}
}
}
}
sub create_lib_directory
{
my @dirs = (
"doc",
"example",
"test");
our $boost_dir;
foreach my $dir (@dirs)
{
mkpath("$boost_dir/libs/asio/$dir");
}
}
sub copy_unit_tests
{
my @dirs = (
"src/tests/unit",
"src/tests/unit/archetypes",
"src/tests/unit/execution",
"src/tests/unit/experimental",
"src/tests/unit/experimental/coro",
"src/tests/unit/generic",
"src/tests/unit/ip",
"src/tests/unit/local",
"src/tests/unit/posix",
"src/tests/unit/ssl",
"src/tests/unit/ts",
"src/tests/unit/windows");
our $boost_dir;
foreach my $dir (@dirs)
{
my @files = ( glob("$dir/*.*pp"), glob("$dir/Jamfile*") );
foreach my $file (@files)
{
if ($file ne "src/tests/unit/thread.cpp"
and $file ne "src/tests/unit/error_handler.cpp"
and $file ne "src/tests/unit/unit_test.cpp")
{
my $from = $file;
my $to = $file;
$to =~ s/^src\/tests\/unit\//$boost_dir\/libs\/asio\/test\//;
copy_source_file($from, $to);
}
}
}
}
sub copy_latency_tests
{
my @dirs = (
"src/tests/latency");
our $boost_dir;
foreach my $dir (@dirs)
{
my @files = ( glob("$dir/*.*pp"), glob("$dir/Jamfile*") );
foreach my $file (@files)
{
my $from = $file;
my $to = $file;
$to =~ s/^src\/tests\/latency\//$boost_dir\/libs\/asio\/test\/latency\//;
copy_source_file($from, $to);
}
}
}
sub copy_properties_tests
{
my @dirs = (
"src/tests/properties/cpp03",
"src/tests/properties/cpp11",
"src/tests/properties/cpp14");
our $boost_dir;
foreach my $dir (@dirs)
{
my @files = ( glob("$dir/*.*pp"), glob("$dir/Jamfile*") );
foreach my $file (@files)
{
my $from = $file;
my $to = $file;
$to =~ s/^src\/tests\/properties\//$boost_dir\/libs\/asio\/test\/properties\//;
copy_source_file($from, $to);
}
}
}
sub copy_examples
{
my @dirs = (
"src/examples/cpp11/allocation",
"src/examples/cpp11/buffers",
"src/examples/cpp11/chat",
"src/examples/cpp11/deferred",
"src/examples/cpp11/echo",
"src/examples/cpp11/executors",
"src/examples/cpp11/files",
"src/examples/cpp11/fork",
"src/examples/cpp11/futures",
"src/examples/cpp11/handler_tracking",
"src/examples/cpp11/http/client",
"src/examples/cpp11/http/doc_root",
"src/examples/cpp11/http/server",
"src/examples/cpp11/http/server2",
"src/examples/cpp11/http/server3",
"src/examples/cpp11/http/server4",
"src/examples/cpp11/icmp",
"src/examples/cpp11/invocation",
"src/examples/cpp11/iostreams",
"src/examples/cpp11/local",
"src/examples/cpp11/multicast",
"src/examples/cpp11/nonblocking",
"src/examples/cpp11/operations",
"src/examples/cpp11/parallel_group",
"src/examples/cpp11/porthopper",
"src/examples/cpp11/serialization",
"src/examples/cpp11/services",
"src/examples/cpp11/socks4",
"src/examples/cpp11/spawn",
"src/examples/cpp11/ssl",
"src/examples/cpp11/timeouts",
"src/examples/cpp11/timers",
"src/examples/cpp11/tutorial",
"src/examples/cpp11/tutorial/daytime1",
"src/examples/cpp11/tutorial/daytime2",
"src/examples/cpp11/tutorial/daytime3",
"src/examples/cpp11/tutorial/daytime4",
"src/examples/cpp11/tutorial/daytime5",
"src/examples/cpp11/tutorial/daytime6",
"src/examples/cpp11/tutorial/daytime7",
"src/examples/cpp11/tutorial/timer1",
"src/examples/cpp11/tutorial/timer2",
"src/examples/cpp11/tutorial/timer3",
"src/examples/cpp11/tutorial/timer4",
"src/examples/cpp11/tutorial/timer5",
"src/examples/cpp11/type_erasure",
"src/examples/cpp11/windows",
"src/examples/cpp14/deferred",
"src/examples/cpp14/echo",
"src/examples/cpp14/executors",
"src/examples/cpp14/iostreams",
"src/examples/cpp14/operations",
"src/examples/cpp14/parallel_group",
"src/examples/cpp17/coroutines_ts",
"src/examples/cpp20/channels",
"src/examples/cpp20/coroutines",
"src/examples/cpp20/invocation",
"src/examples/cpp20/operations",
"src/examples/cpp20/type_erasure");
our $boost_dir;
foreach my $dir (@dirs)
{
my @files = (
glob("$dir/*.*pp"),
glob("$dir/*.html"),
glob("$dir/Jamfile*"),
glob("$dir/*.pem"),
glob("$dir/README*"),
glob("$dir/*.txt"));
foreach my $file (@files)
{
my $from = $file;
my $to = $file;
$to =~ s/^src\/examples\//$boost_dir\/libs\/asio\/example\//;
copy_source_file($from, $to);
}
}
}
sub copy_doc
{
our $boost_dir;
my @files = (
"src/doc/asio.qbk",
"src/doc/examples.qbk",
"src/doc/net_ts.qbk",
"src/doc/overview.qbk",
"src/doc/quickref.xml",
"src/doc/reference.xsl",
"src/doc/std_executors.qbk",
"src/doc/tutorial.xsl",
glob("src/doc/overview/*.qbk"),
glob("src/doc/overview/model/*.qbk"),
glob("src/doc/requirements/*.qbk"));
foreach my $file (@files)
{
my $from = $file;
my $to = $file;
$to =~ s/^src\/doc\//$boost_dir\/libs\/asio\/doc\//;
copy_source_file($from, $to);
}
}
sub copy_tools
{
our $boost_dir;
my @files = (
glob("src/tools/*.pl"));
foreach my $file (@files)
{
my $from = $file;
my $to = $file;
$to =~ s/^src\/tools\//$boost_dir\/libs\/asio\/tools\//;
copy_source_file($from, $to);
}
}
my $includes_only = 0;
if (scalar(@ARGV) == 1 && $ARGV[0] eq "--includes-only")
{
$includes_only = 1;
}
copy_include_files();
if (not $includes_only)
{
create_lib_directory();
copy_unit_tests();
copy_latency_tests();
copy_properties_tests();
copy_examples();
copy_doc();
copy_tools();
}
exit($bad_lines > 0 ? 1 : 0);

View File

@@ -1,259 +0,0 @@
AC_INIT([asio],[1.31.0])
AC_CONFIG_SRCDIR(include/asio.hpp)
AM_MAINTAINER_MODE
AM_INIT_AUTOMAKE([tar-pax])
AC_CANONICAL_HOST
AM_PROG_CC_C_O
AC_PROG_CXX
AC_LANG(C++)
AC_PROG_RANLIB
PKG_INSTALLDIR
AC_DEFINE(_REENTRANT, [1], [Define this])
AC_ARG_WITH(boost,
AS_HELP_STRING([--with-boost=DIR],[location of boost distribution]),
[
if test "${withval}" = no; then
STANDALONE="yes"
else
if test "${withval}" != system; then
CPPFLAGS="$CPPFLAGS -I${withval}"
LIBS="$LIBS -L${withval}/stage/lib"
fi
CPPFLAGS="$CPPFLAGS -DASIO_ENABLE_BOOST -DBOOST_CHRONO_HEADER_ONLY -DBOOST_CHRONO_DONT_PROVIDE_HYBRID_ERROR_HANDLING"
fi
],
[
STANDALONE="yes"
])
AC_ARG_ENABLE(separate-compilation,
[ --enable-separate-compilation separate compilation of asio source],
[
SEPARATE_COMPILATION=yes
])
AC_ARG_ENABLE(boost-coroutine,
[ --enable-boost-coroutine use Boost.Coroutine to implement stackful coroutines],
[
HAVE_BOOST_COROUTINE=yes
])
if test "$STANDALONE" != yes; then
AC_CHECK_HEADER([boost/noncopyable.hpp],,
[
echo "Can't find boost headers. Please check the location of the boost"
echo "distribution and rerun configure using the --with-boost=DIR option."
echo "Alternatively, run with --without-boost to enable standalone build."
exit 1
],[])
fi
AC_ARG_WITH(openssl,
AS_HELP_STRING([--with-openssl=DIR],[location of openssl]),
[
CPPFLAGS="$CPPFLAGS -I${withval}/include"
LIBS="$LIBS -L${withval}/lib"
],[])
AC_CHECK_HEADER([openssl/ssl.h],,
[
OPENSSL_FOUND=no
],[])
if test x$OPENSSL_FOUND != xno; then
LIBS="$LIBS -lssl -lcrypto"
fi
AM_CONDITIONAL(HAVE_OPENSSL,test x$OPENSSL_FOUND != xno)
WINDOWS=no
case $host in
*-*-linux*)
CXXFLAGS="$CXXFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread"
LIBS="$LIBS -lrt"
;;
*-*-solaris*)
if test "$GXX" = yes; then
CXXFLAGS="$CXXFLAGS -D_PTHREADS"
else
# We'll assume Sun's CC.
CXXFLAGS="$CXXFLAGS -mt"
fi
LIBS="$LIBS -lsocket -lnsl -lpthread"
;;
*-*-mingw32*)
CXXFLAGS="$CXXFLAGS -mthreads"
LDFLAGS="$LDFLAGS -mthreads"
LIBS="$LIBS -lws2_32 -lmswsock"
WINDOWS=yes
;;
*-*-mingw64*)
CXXFLAGS="$CXXFLAGS -mthreads"
LDFLAGS="$LDFLAGS -mthreads"
LIBS="$LIBS -lws2_32 -lmswsock"
WINDOWS=yes
;;
*-pc-cygwin*)
CXXFLAGS="$CXXFLAGS -D__USE_W32_SOCKETS -D_WIN32_WINNT=0x0601"
LIBS="$LIBS -lws2_32 -lmswsock"
WINDOWS=yes
;;
*-apple-darwin*)
CXXFLAGS="$CXXFLAGS"
LDFLAGS="$LDFLAGS"
;;
*-*-freebsd*)
CXXFLAGS="$CXXFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread"
;;
*-*-netbsd*)
CXXFLAGS="$CXXFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread"
;;
*-*-haiku*)
CXXFLAGS="$CXXFLAGS -lnetwork"
LDFLAGS="$LDFLAGS -lnetwork"
esac
if test "$GXX" = yes; then
CXXFLAGS="$CXXFLAGS -ftemplate-depth-256"
fi
if test "$STANDALONE" = yes; then
CPPFLAGS="$CPPFLAGS -DASIO_STANDALONE"
fi
if test "$SEPARATE_COMPILATION" = yes; then
CPPFLAGS="$CPPFLAGS -DASIO_SEPARATE_COMPILATION"
fi
AC_MSG_CHECKING([whether C++11 is enabled])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[#if __cplusplus < 201103L]]
[[#error C++11 not available]]
[[#endif]])],
[AC_MSG_RESULT([yes])
HAVE_CXX11=yes;],
[AC_MSG_RESULT([no])
HAVE_CXX11=no;])
AC_MSG_CHECKING([whether C++14 is enabled])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[#if defined(__GNUC__) && !defined(__clang__)]]
[[# if (__GNUC__ <= 6)]]
[[# error C++14 support on this compiler not sufficiently compliant]]
[[# endif]]
[[#endif]]
[[#if __cplusplus < 201402L]]
[[#error C++14 not available]]
[[#endif]])],
[AC_MSG_RESULT([yes])
HAVE_CXX14=yes;],
[AC_MSG_RESULT([no])
HAVE_CXX14=no;])
AC_MSG_CHECKING([whether C++17 is enabled])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[#if __cplusplus < 201703L]]
[[#error C++17 not available]]
[[#endif]])],
[AC_MSG_RESULT([yes])
HAVE_CXX17=yes;],
[AC_MSG_RESULT([no])
HAVE_CXX17=no;])
AC_MSG_CHECKING([whether C++20 is enabled])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[#if __cplusplus < 202002L]]
[[#error C++20 not available]]
[[#endif]])],
[AC_MSG_RESULT([yes])
HAVE_CXX20=yes;],
[AC_MSG_RESULT([no])
HAVE_CXX20=no;])
AC_MSG_CHECKING([whether coroutines are enabled])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[#if defined(__clang__)]]
[[# if (__clang_major__ >= 14)]]
[[# if (__cplusplus >= 202002) && (__cpp_impl_coroutine >= 201902)]]
[[# if __has_include(<coroutine>)]]
[[# define ASIO_HAS_CO_AWAIT 1]]
[[# endif]]
[[# elif (__cplusplus >= 201703) && (__cpp_coroutines >= 201703)]]
[[# if __has_include(<experimental/coroutine>)]]
[[# define ASIO_HAS_CO_AWAIT 1]]
[[# endif]]
[[# endif]]
[[# else]]
[[# if (__cplusplus >= 201703) && (__cpp_coroutines >= 201703)]]
[[# if __has_include(<experimental/coroutine>)]]
[[# define ASIO_HAS_CO_AWAIT 1]]
[[# endif]]
[[# endif]]
[[# endif]]
[[#elif defined(__GNUC__)]]
[[# if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)]]
[[# if __has_include(<coroutine>)]]
[[# define ASIO_HAS_CO_AWAIT 1]]
[[# endif]]
[[# endif]]
[[#endif]]
[[#ifndef ASIO_HAS_CO_AWAIT]]
[[# error coroutines not available]]
[[#endif]])],
[AC_MSG_RESULT([yes])
HAVE_COROUTINES=yes;],
[AC_MSG_RESULT([no])
HAVE_COROUTINES=no;])
if test "$GXX" = yes; then
if test "$STANDALONE" = yes; then
if test "$HAVE_CXX11" = no; then
HAVE_CXX11=yes
CPPFLAGS="-std=c++0x $CPPFLAGS"
fi
fi
fi
AM_CONDITIONAL(STANDALONE,test x$STANDALONE = xyes)
AM_CONDITIONAL(SEPARATE_COMPILATION,test x$SEPARATE_COMPILATION = xyes)
AM_CONDITIONAL(HAVE_BOOST_COROUTINE,test x$HAVE_BOOST_COROUTINE = xyes)
AM_CONDITIONAL(WINDOWS_TARGET,test x$WINDOWS != xno)
AM_CONDITIONAL(HAVE_CXX11,test x$HAVE_CXX11 = xyes)
AM_CONDITIONAL(HAVE_CXX14,test x$HAVE_CXX14 = xyes)
AM_CONDITIONAL(HAVE_CXX17,test x$HAVE_CXX17 = xyes)
AM_CONDITIONAL(HAVE_CXX20,test x$HAVE_CXX20 = xyes)
AM_CONDITIONAL(HAVE_COROUTINES,test x$HAVE_COROUTINES = xyes)
AC_CONFIG_FILES([asio.pc])
AC_CONFIG_FILES([
Makefile
include/Makefile
src/Makefile
src/tests/Makefile
src/tests/properties/Makefile
src/examples/cpp11/Makefile
src/examples/cpp14/Makefile
src/examples/cpp17/Makefile
src/examples/cpp20/Makefile])
AC_OUTPUT

View File

@@ -1,2 +0,0 @@
Makefile
Makefile.in

View File

@@ -1,635 +0,0 @@
# find . -name "*.*pp" | sed -e 's/^\.\///' | sed -e 's/^.*$/ & \\/' | sort
nobase_include_HEADERS = \
asio/any_completion_executor.hpp \
asio/any_completion_handler.hpp \
asio/any_io_executor.hpp \
asio/append.hpp \
asio/as_tuple.hpp \
asio/associated_allocator.hpp \
asio/associated_cancellation_slot.hpp \
asio/associated_executor.hpp \
asio/associated_immediate_executor.hpp \
asio/associator.hpp \
asio/async_result.hpp \
asio/awaitable.hpp \
asio/basic_datagram_socket.hpp \
asio/basic_deadline_timer.hpp \
asio/basic_file.hpp \
asio/basic_io_object.hpp \
asio/basic_random_access_file.hpp \
asio/basic_raw_socket.hpp \
asio/basic_readable_pipe.hpp \
asio/basic_seq_packet_socket.hpp \
asio/basic_serial_port.hpp \
asio/basic_signal_set.hpp \
asio/basic_socket_acceptor.hpp \
asio/basic_socket.hpp \
asio/basic_socket_iostream.hpp \
asio/basic_socket_streambuf.hpp \
asio/basic_streambuf_fwd.hpp \
asio/basic_streambuf.hpp \
asio/basic_stream_file.hpp \
asio/basic_stream_socket.hpp \
asio/basic_waitable_timer.hpp \
asio/basic_writable_pipe.hpp \
asio/bind_allocator.hpp \
asio/bind_cancellation_slot.hpp \
asio/bind_executor.hpp \
asio/bind_immediate_executor.hpp \
asio/buffered_read_stream_fwd.hpp \
asio/buffered_read_stream.hpp \
asio/buffered_stream_fwd.hpp \
asio/buffered_stream.hpp \
asio/buffered_write_stream_fwd.hpp \
asio/buffered_write_stream.hpp \
asio/buffer.hpp \
asio/buffer_registration.hpp \
asio/buffers_iterator.hpp \
asio/cancel_after.hpp \
asio/cancel_at.hpp \
asio/cancellation_signal.hpp \
asio/cancellation_state.hpp \
asio/cancellation_type.hpp \
asio/co_composed.hpp \
asio/co_spawn.hpp \
asio/completion_condition.hpp \
asio/compose.hpp \
asio/composed.hpp \
asio/connect.hpp \
asio/connect_pipe.hpp \
asio/consign.hpp \
asio/coroutine.hpp \
asio/deadline_timer.hpp \
asio/defer.hpp \
asio/deferred.hpp \
asio/default_completion_token.hpp \
asio/detached.hpp \
asio/detail/array_fwd.hpp \
asio/detail/array.hpp \
asio/detail/assert.hpp \
asio/detail/atomic_count.hpp \
asio/detail/base_from_cancellation_state.hpp \
asio/detail/base_from_completion_cond.hpp \
asio/detail/bind_handler.hpp \
asio/detail/blocking_executor_op.hpp \
asio/detail/buffered_stream_storage.hpp \
asio/detail/buffer_resize_guard.hpp \
asio/detail/buffer_sequence_adapter.hpp \
asio/detail/call_stack.hpp \
asio/detail/chrono.hpp \
asio/detail/chrono_time_traits.hpp \
asio/detail/completion_handler.hpp \
asio/detail/completion_message.hpp \
asio/detail/completion_payload.hpp \
asio/detail/completion_payload_handler.hpp \
asio/detail/composed_work.hpp \
asio/detail/concurrency_hint.hpp \
asio/detail/conditionally_enabled_event.hpp \
asio/detail/conditionally_enabled_mutex.hpp \
asio/detail/config.hpp \
asio/detail/consuming_buffers.hpp \
asio/detail/cstddef.hpp \
asio/detail/cstdint.hpp \
asio/detail/date_time_fwd.hpp \
asio/detail/deadline_timer_service.hpp \
asio/detail/dependent_type.hpp \
asio/detail/descriptor_ops.hpp \
asio/detail/descriptor_read_op.hpp \
asio/detail/descriptor_write_op.hpp \
asio/detail/dev_poll_reactor.hpp \
asio/detail/epoll_reactor.hpp \
asio/detail/eventfd_select_interrupter.hpp \
asio/detail/event.hpp \
asio/detail/exception.hpp \
asio/detail/executor_function.hpp \
asio/detail/executor_op.hpp \
asio/detail/fd_set_adapter.hpp \
asio/detail/fenced_block.hpp \
asio/detail/functional.hpp \
asio/detail/future.hpp \
asio/detail/global.hpp \
asio/detail/handler_alloc_helpers.hpp \
asio/detail/handler_cont_helpers.hpp \
asio/detail/handler_tracking.hpp \
asio/detail/handler_type_requirements.hpp \
asio/detail/handler_work.hpp \
asio/detail/hash_map.hpp \
asio/detail/impl/buffer_sequence_adapter.ipp \
asio/detail/impl/descriptor_ops.ipp \
asio/detail/impl/dev_poll_reactor.hpp \
asio/detail/impl/dev_poll_reactor.ipp \
asio/detail/impl/epoll_reactor.hpp \
asio/detail/impl/epoll_reactor.ipp \
asio/detail/impl/eventfd_select_interrupter.ipp \
asio/detail/impl/handler_tracking.ipp \
asio/detail/impl/io_uring_descriptor_service.ipp \
asio/detail/impl/io_uring_file_service.ipp \
asio/detail/impl/io_uring_service.hpp \
asio/detail/impl/io_uring_service.ipp \
asio/detail/impl/io_uring_socket_service_base.ipp \
asio/detail/impl/kqueue_reactor.hpp \
asio/detail/impl/kqueue_reactor.ipp \
asio/detail/impl/null_event.ipp \
asio/detail/impl/pipe_select_interrupter.ipp \
asio/detail/impl/posix_event.ipp \
asio/detail/impl/posix_mutex.ipp \
asio/detail/impl/posix_serial_port_service.ipp \
asio/detail/impl/posix_thread.ipp \
asio/detail/impl/posix_tss_ptr.ipp \
asio/detail/impl/reactive_descriptor_service.ipp \
asio/detail/impl/reactive_socket_service_base.ipp \
asio/detail/impl/resolver_service_base.ipp \
asio/detail/impl/scheduler.ipp \
asio/detail/impl/select_reactor.hpp \
asio/detail/impl/select_reactor.ipp \
asio/detail/impl/service_registry.hpp \
asio/detail/impl/service_registry.ipp \
asio/detail/impl/signal_set_service.ipp \
asio/detail/impl/socket_ops.ipp \
asio/detail/impl/socket_select_interrupter.ipp \
asio/detail/impl/strand_executor_service.hpp \
asio/detail/impl/strand_executor_service.ipp \
asio/detail/impl/strand_service.hpp \
asio/detail/impl/strand_service.ipp \
asio/detail/impl/thread_context.ipp \
asio/detail/impl/throw_error.ipp \
asio/detail/impl/timer_queue_ptime.ipp \
asio/detail/impl/timer_queue_set.ipp \
asio/detail/impl/win_event.ipp \
asio/detail/impl/win_iocp_file_service.ipp \
asio/detail/impl/win_iocp_handle_service.ipp \
asio/detail/impl/win_iocp_io_context.hpp \
asio/detail/impl/win_iocp_io_context.ipp \
asio/detail/impl/win_iocp_serial_port_service.ipp \
asio/detail/impl/win_iocp_socket_service_base.ipp \
asio/detail/impl/win_mutex.ipp \
asio/detail/impl/win_object_handle_service.ipp \
asio/detail/impl/winrt_ssocket_service_base.ipp \
asio/detail/impl/winrt_timer_scheduler.hpp \
asio/detail/impl/winrt_timer_scheduler.ipp \
asio/detail/impl/winsock_init.ipp \
asio/detail/impl/win_static_mutex.ipp \
asio/detail/impl/win_thread.ipp \
asio/detail/impl/win_tss_ptr.ipp \
asio/detail/initiate_defer.hpp \
asio/detail/initiate_dispatch.hpp \
asio/detail/initiate_post.hpp \
asio/detail/initiation_base.hpp \
asio/detail/io_control.hpp \
asio/detail/io_object_impl.hpp \
asio/detail/io_uring_descriptor_read_at_op.hpp \
asio/detail/io_uring_descriptor_read_op.hpp \
asio/detail/io_uring_descriptor_service.hpp \
asio/detail/io_uring_descriptor_write_at_op.hpp \
asio/detail/io_uring_descriptor_write_op.hpp \
asio/detail/io_uring_file_service.hpp \
asio/detail/io_uring_null_buffers_op.hpp \
asio/detail/io_uring_operation.hpp \
asio/detail/io_uring_service.hpp \
asio/detail/io_uring_socket_accept_op.hpp \
asio/detail/io_uring_socket_connect_op.hpp \
asio/detail/io_uring_socket_recvfrom_op.hpp \
asio/detail/io_uring_socket_recvmsg_op.hpp \
asio/detail/io_uring_socket_recv_op.hpp \
asio/detail/io_uring_socket_send_op.hpp \
asio/detail/io_uring_socket_sendto_op.hpp \
asio/detail/io_uring_socket_service_base.hpp \
asio/detail/io_uring_socket_service.hpp \
asio/detail/io_uring_wait_op.hpp \
asio/detail/is_buffer_sequence.hpp \
asio/detail/is_executor.hpp \
asio/detail/keyword_tss_ptr.hpp \
asio/detail/kqueue_reactor.hpp \
asio/detail/limits.hpp \
asio/detail/local_free_on_block_exit.hpp \
asio/detail/memory.hpp \
asio/detail/mutex.hpp \
asio/detail/non_const_lvalue.hpp \
asio/detail/noncopyable.hpp \
asio/detail/null_event.hpp \
asio/detail/null_fenced_block.hpp \
asio/detail/null_global.hpp \
asio/detail/null_mutex.hpp \
asio/detail/null_reactor.hpp \
asio/detail/null_signal_blocker.hpp \
asio/detail/null_socket_service.hpp \
asio/detail/null_static_mutex.hpp \
asio/detail/null_thread.hpp \
asio/detail/null_tss_ptr.hpp \
asio/detail/object_pool.hpp \
asio/detail/old_win_sdk_compat.hpp \
asio/detail/operation.hpp \
asio/detail/op_queue.hpp \
asio/detail/pipe_select_interrupter.hpp \
asio/detail/pop_options.hpp \
asio/detail/posix_event.hpp \
asio/detail/posix_fd_set_adapter.hpp \
asio/detail/posix_global.hpp \
asio/detail/posix_mutex.hpp \
asio/detail/posix_serial_port_service.hpp \
asio/detail/posix_signal_blocker.hpp \
asio/detail/posix_static_mutex.hpp \
asio/detail/posix_thread.hpp \
asio/detail/posix_tss_ptr.hpp \
asio/detail/push_options.hpp \
asio/detail/reactive_descriptor_service.hpp \
asio/detail/reactive_null_buffers_op.hpp \
asio/detail/reactive_socket_accept_op.hpp \
asio/detail/reactive_socket_connect_op.hpp \
asio/detail/reactive_socket_recvfrom_op.hpp \
asio/detail/reactive_socket_recvmsg_op.hpp \
asio/detail/reactive_socket_recv_op.hpp \
asio/detail/reactive_socket_send_op.hpp \
asio/detail/reactive_socket_sendto_op.hpp \
asio/detail/reactive_socket_service_base.hpp \
asio/detail/reactive_socket_service.hpp \
asio/detail/reactive_wait_op.hpp \
asio/detail/reactor.hpp \
asio/detail/reactor_op.hpp \
asio/detail/reactor_op_queue.hpp \
asio/detail/recycling_allocator.hpp \
asio/detail/regex_fwd.hpp \
asio/detail/resolve_endpoint_op.hpp \
asio/detail/resolve_op.hpp \
asio/detail/resolve_query_op.hpp \
asio/detail/resolver_service_base.hpp \
asio/detail/resolver_service.hpp \
asio/detail/scheduler.hpp \
asio/detail/scheduler_operation.hpp \
asio/detail/scheduler_task.hpp \
asio/detail/scheduler_thread_info.hpp \
asio/detail/scoped_lock.hpp \
asio/detail/scoped_ptr.hpp \
asio/detail/select_interrupter.hpp \
asio/detail/select_reactor.hpp \
asio/detail/service_registry.hpp \
asio/detail/signal_blocker.hpp \
asio/detail/signal_handler.hpp \
asio/detail/signal_init.hpp \
asio/detail/signal_op.hpp \
asio/detail/signal_set_service.hpp \
asio/detail/socket_holder.hpp \
asio/detail/socket_ops.hpp \
asio/detail/socket_option.hpp \
asio/detail/socket_select_interrupter.hpp \
asio/detail/socket_types.hpp \
asio/detail/source_location.hpp \
asio/detail/static_mutex.hpp \
asio/detail/std_event.hpp \
asio/detail/std_fenced_block.hpp \
asio/detail/std_global.hpp \
asio/detail/std_mutex.hpp \
asio/detail/std_static_mutex.hpp \
asio/detail/std_thread.hpp \
asio/detail/strand_executor_service.hpp \
asio/detail/strand_service.hpp \
asio/detail/string_view.hpp \
asio/detail/thread_context.hpp \
asio/detail/thread_group.hpp \
asio/detail/thread.hpp \
asio/detail/thread_info_base.hpp \
asio/detail/throw_error.hpp \
asio/detail/throw_exception.hpp \
asio/detail/timed_cancel_op.hpp \
asio/detail/timer_queue_base.hpp \
asio/detail/timer_queue.hpp \
asio/detail/timer_queue_ptime.hpp \
asio/detail/timer_queue_set.hpp \
asio/detail/timer_scheduler_fwd.hpp \
asio/detail/timer_scheduler.hpp \
asio/detail/tss_ptr.hpp \
asio/detail/type_traits.hpp \
asio/detail/utility.hpp \
asio/detail/wait_handler.hpp \
asio/detail/wait_op.hpp \
asio/detail/winapp_thread.hpp \
asio/detail/wince_thread.hpp \
asio/detail/win_event.hpp \
asio/detail/win_fd_set_adapter.hpp \
asio/detail/win_global.hpp \
asio/detail/win_iocp_file_service.hpp \
asio/detail/win_iocp_handle_read_op.hpp \
asio/detail/win_iocp_handle_service.hpp \
asio/detail/win_iocp_handle_write_op.hpp \
asio/detail/win_iocp_io_context.hpp \
asio/detail/win_iocp_null_buffers_op.hpp \
asio/detail/win_iocp_operation.hpp \
asio/detail/win_iocp_overlapped_op.hpp \
asio/detail/win_iocp_overlapped_ptr.hpp \
asio/detail/win_iocp_serial_port_service.hpp \
asio/detail/win_iocp_socket_accept_op.hpp \
asio/detail/win_iocp_socket_connect_op.hpp \
asio/detail/win_iocp_socket_recvfrom_op.hpp \
asio/detail/win_iocp_socket_recvmsg_op.hpp \
asio/detail/win_iocp_socket_recv_op.hpp \
asio/detail/win_iocp_socket_send_op.hpp \
asio/detail/win_iocp_socket_service_base.hpp \
asio/detail/win_iocp_socket_service.hpp \
asio/detail/win_iocp_thread_info.hpp \
asio/detail/win_iocp_wait_op.hpp \
asio/detail/win_mutex.hpp \
asio/detail/win_object_handle_service.hpp \
asio/detail/winrt_async_manager.hpp \
asio/detail/winrt_async_op.hpp \
asio/detail/winrt_resolve_op.hpp \
asio/detail/winrt_resolver_service.hpp \
asio/detail/winrt_socket_connect_op.hpp \
asio/detail/winrt_socket_recv_op.hpp \
asio/detail/winrt_socket_send_op.hpp \
asio/detail/winrt_ssocket_service_base.hpp \
asio/detail/winrt_ssocket_service.hpp \
asio/detail/winrt_timer_scheduler.hpp \
asio/detail/winrt_utils.hpp \
asio/detail/winsock_init.hpp \
asio/detail/win_static_mutex.hpp \
asio/detail/win_thread.hpp \
asio/detail/win_tss_ptr.hpp \
asio/detail/work_dispatcher.hpp \
asio/detail/wrapped_handler.hpp \
asio/dispatch.hpp \
asio/error_code.hpp \
asio/error.hpp \
asio/execution.hpp \
asio/execution_context.hpp \
asio/execution/allocator.hpp \
asio/execution/any_executor.hpp \
asio/execution/bad_executor.hpp \
asio/execution/blocking.hpp \
asio/execution/blocking_adaptation.hpp \
asio/execution/context.hpp \
asio/execution/context_as.hpp \
asio/execution/executor.hpp \
asio/execution/impl/bad_executor.ipp \
asio/execution/invocable_archetype.hpp \
asio/execution/mapping.hpp \
asio/execution/occupancy.hpp \
asio/execution/outstanding_work.hpp \
asio/execution/prefer_only.hpp \
asio/execution/relationship.hpp \
asio/executor.hpp \
asio/executor_work_guard.hpp \
asio/experimental/append.hpp \
asio/experimental/as_single.hpp \
asio/experimental/as_tuple.hpp \
asio/experimental/awaitable_operators.hpp \
asio/experimental/basic_channel.hpp \
asio/experimental/basic_concurrent_channel.hpp \
asio/experimental/cancellation_condition.hpp \
asio/experimental/channel.hpp \
asio/experimental/channel_error.hpp \
asio/experimental/channel_traits.hpp \
asio/experimental/co_composed.hpp \
asio/experimental/co_spawn.hpp \
asio/experimental/concurrent_channel.hpp \
asio/experimental/coro.hpp \
asio/experimental/coro_traits.hpp \
asio/experimental/deferred.hpp \
asio/experimental/detail/channel_operation.hpp \
asio/experimental/detail/channel_receive_op.hpp \
asio/experimental/detail/channel_send_functions.hpp \
asio/experimental/detail/channel_send_op.hpp \
asio/experimental/detail/channel_service.hpp \
asio/experimental/detail/coro_completion_handler.hpp \
asio/experimental/detail/coro_promise_allocator.hpp \
asio/experimental/detail/has_signature.hpp \
asio/experimental/detail/impl/channel_service.hpp \
asio/experimental/detail/partial_promise.hpp \
asio/experimental/impl/as_single.hpp \
asio/experimental/impl/channel_error.ipp \
asio/experimental/impl/coro.hpp \
asio/experimental/impl/parallel_group.hpp \
asio/experimental/impl/promise.hpp \
asio/experimental/impl/use_coro.hpp \
asio/experimental/impl/use_promise.hpp \
asio/experimental/parallel_group.hpp \
asio/experimental/prepend.hpp \
asio/experimental/promise.hpp \
asio/experimental/use_coro.hpp \
asio/experimental/use_promise.hpp \
asio/file_base.hpp \
asio/generic/basic_endpoint.hpp \
asio/generic/datagram_protocol.hpp \
asio/generic/detail/endpoint.hpp \
asio/generic/detail/impl/endpoint.ipp \
asio/generic/raw_protocol.hpp \
asio/generic/seq_packet_protocol.hpp \
asio/generic/stream_protocol.hpp \
asio/handler_continuation_hook.hpp \
asio/high_resolution_timer.hpp \
asio.hpp \
asio/immediate.hpp \
asio/impl/any_completion_executor.ipp \
asio/impl/any_io_executor.ipp \
asio/impl/append.hpp \
asio/impl/as_tuple.hpp \
asio/impl/awaitable.hpp \
asio/impl/buffered_read_stream.hpp \
asio/impl/buffered_write_stream.hpp \
asio/impl/cancel_after.hpp \
asio/impl/cancel_at.hpp \
asio/impl/cancellation_signal.ipp \
asio/impl/co_spawn.hpp \
asio/impl/connect.hpp \
asio/impl/connect_pipe.hpp \
asio/impl/connect_pipe.ipp \
asio/impl/consign.hpp \
asio/impl/deferred.hpp \
asio/impl/detached.hpp \
asio/impl/error_code.ipp \
asio/impl/error.ipp \
asio/impl/execution_context.hpp \
asio/impl/execution_context.ipp \
asio/impl/executor.hpp \
asio/impl/executor.ipp \
asio/impl/io_context.hpp \
asio/impl/io_context.ipp \
asio/impl/multiple_exceptions.ipp \
asio/impl/prepend.hpp \
asio/impl/read_at.hpp \
asio/impl/read.hpp \
asio/impl/read_until.hpp \
asio/impl/redirect_error.hpp \
asio/impl/serial_port_base.hpp \
asio/impl/serial_port_base.ipp \
asio/impl/spawn.hpp \
asio/impl/src.hpp \
asio/impl/system_context.hpp \
asio/impl/system_context.ipp \
asio/impl/system_executor.hpp \
asio/impl/thread_pool.hpp \
asio/impl/thread_pool.ipp \
asio/impl/use_awaitable.hpp \
asio/impl/use_future.hpp \
asio/impl/write_at.hpp \
asio/impl/write.hpp \
asio/io_context.hpp \
asio/io_context_strand.hpp \
asio/io_service.hpp \
asio/io_service_strand.hpp \
asio/ip/address.hpp \
asio/ip/address_v4.hpp \
asio/ip/address_v4_iterator.hpp \
asio/ip/address_v4_range.hpp \
asio/ip/address_v6.hpp \
asio/ip/address_v6_iterator.hpp \
asio/ip/address_v6_range.hpp \
asio/ip/bad_address_cast.hpp \
asio/ip/basic_endpoint.hpp \
asio/ip/basic_resolver_entry.hpp \
asio/ip/basic_resolver.hpp \
asio/ip/basic_resolver_iterator.hpp \
asio/ip/basic_resolver_query.hpp \
asio/ip/basic_resolver_results.hpp \
asio/ip/detail/endpoint.hpp \
asio/ip/detail/impl/endpoint.ipp \
asio/ip/detail/socket_option.hpp \
asio/ip/host_name.hpp \
asio/ip/icmp.hpp \
asio/ip/impl/address.hpp \
asio/ip/impl/address.ipp \
asio/ip/impl/address_v4.hpp \
asio/ip/impl/address_v4.ipp \
asio/ip/impl/address_v6.hpp \
asio/ip/impl/address_v6.ipp \
asio/ip/impl/basic_endpoint.hpp \
asio/ip/impl/host_name.ipp \
asio/ip/impl/network_v4.hpp \
asio/ip/impl/network_v4.ipp \
asio/ip/impl/network_v6.hpp \
asio/ip/impl/network_v6.ipp \
asio/ip/multicast.hpp \
asio/ip/network_v4.hpp \
asio/ip/network_v6.hpp \
asio/ip/resolver_base.hpp \
asio/ip/resolver_query_base.hpp \
asio/ip/tcp.hpp \
asio/ip/udp.hpp \
asio/ip/unicast.hpp \
asio/ip/v6_only.hpp \
asio/is_applicable_property.hpp \
asio/is_contiguous_iterator.hpp \
asio/is_executor.hpp \
asio/is_read_buffered.hpp \
asio/is_write_buffered.hpp \
asio/local/basic_endpoint.hpp \
asio/local/connect_pair.hpp \
asio/local/datagram_protocol.hpp \
asio/local/detail/endpoint.hpp \
asio/local/detail/impl/endpoint.ipp \
asio/local/seq_packet_protocol.hpp \
asio/local/stream_protocol.hpp \
asio/multiple_exceptions.hpp \
asio/packaged_task.hpp \
asio/placeholders.hpp \
asio/posix/basic_descriptor.hpp \
asio/posix/basic_stream_descriptor.hpp \
asio/posix/descriptor_base.hpp \
asio/posix/descriptor.hpp \
asio/posix/stream_descriptor.hpp \
asio/post.hpp \
asio/prefer.hpp \
asio/prepend.hpp \
asio/query.hpp \
asio/random_access_file.hpp \
asio/read_at.hpp \
asio/read.hpp \
asio/read_until.hpp \
asio/readable_pipe.hpp \
asio/recycling_allocator.hpp \
asio/redirect_error.hpp \
asio/registered_buffer.hpp \
asio/require.hpp \
asio/require_concept.hpp \
asio/serial_port_base.hpp \
asio/serial_port.hpp \
asio/signal_set_base.hpp \
asio/signal_set.hpp \
asio/socket_base.hpp \
asio/spawn.hpp \
asio/ssl/context_base.hpp \
asio/ssl/context.hpp \
asio/ssl/detail/buffered_handshake_op.hpp \
asio/ssl/detail/engine.hpp \
asio/ssl/detail/handshake_op.hpp \
asio/ssl/detail/impl/engine.ipp \
asio/ssl/detail/impl/openssl_init.ipp \
asio/ssl/detail/io.hpp \
asio/ssl/detail/openssl_init.hpp \
asio/ssl/detail/openssl_types.hpp \
asio/ssl/detail/password_callback.hpp \
asio/ssl/detail/read_op.hpp \
asio/ssl/detail/shutdown_op.hpp \
asio/ssl/detail/stream_core.hpp \
asio/ssl/detail/verify_callback.hpp \
asio/ssl/detail/write_op.hpp \
asio/ssl/error.hpp \
asio/ssl.hpp \
asio/ssl/host_name_verification.hpp \
asio/ssl/impl/context.hpp \
asio/ssl/impl/context.ipp \
asio/ssl/impl/error.ipp \
asio/ssl/impl/host_name_verification.ipp \
asio/ssl/impl/rfc2818_verification.ipp \
asio/ssl/impl/src.hpp \
asio/ssl/rfc2818_verification.hpp \
asio/ssl/stream_base.hpp \
asio/ssl/stream.hpp \
asio/ssl/verify_context.hpp \
asio/ssl/verify_mode.hpp \
asio/static_thread_pool.hpp \
asio/steady_timer.hpp \
asio/strand.hpp \
asio/streambuf.hpp \
asio/stream_file.hpp \
asio/system_context.hpp \
asio/system_error.hpp \
asio/system_executor.hpp \
asio/system_timer.hpp \
asio/this_coro.hpp \
asio/thread.hpp \
asio/thread_pool.hpp \
asio/time_traits.hpp \
asio/traits/equality_comparable.hpp \
asio/traits/execute_member.hpp \
asio/traits/prefer_free.hpp \
asio/traits/prefer_member.hpp \
asio/traits/query_free.hpp \
asio/traits/query_member.hpp \
asio/traits/query_static_constexpr_member.hpp \
asio/traits/require_concept_free.hpp \
asio/traits/require_concept_member.hpp \
asio/traits/require_free.hpp \
asio/traits/require_member.hpp \
asio/traits/static_query.hpp \
asio/traits/static_require.hpp \
asio/traits/static_require_concept.hpp \
asio/ts/buffer.hpp \
asio/ts/executor.hpp \
asio/ts/internet.hpp \
asio/ts/io_context.hpp \
asio/ts/netfwd.hpp \
asio/ts/net.hpp \
asio/ts/socket.hpp \
asio/ts/timer.hpp \
asio/unyield.hpp \
asio/use_awaitable.hpp \
asio/use_future.hpp \
asio/uses_executor.hpp \
asio/version.hpp \
asio/wait_traits.hpp \
asio/windows/basic_object_handle.hpp \
asio/windows/basic_overlapped_handle.hpp \
asio/windows/basic_random_access_handle.hpp \
asio/windows/basic_stream_handle.hpp \
asio/windows/object_handle.hpp \
asio/windows/overlapped_handle.hpp \
asio/windows/overlapped_ptr.hpp \
asio/windows/random_access_handle.hpp \
asio/windows/stream_handle.hpp \
asio/writable_pipe.hpp \
asio/write_at.hpp \
asio/write.hpp \
asio/yield.hpp
MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in

View File

@@ -1,205 +0,0 @@
//
// asio.hpp
// ~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_HPP
#define ASIO_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/any_completion_executor.hpp"
#include "asio/any_completion_handler.hpp"
#include "asio/any_io_executor.hpp"
#include "asio/append.hpp"
#include "asio/as_tuple.hpp"
#include "asio/associated_allocator.hpp"
#include "asio/associated_cancellation_slot.hpp"
#include "asio/associated_executor.hpp"
#include "asio/associated_immediate_executor.hpp"
#include "asio/associator.hpp"
#include "asio/async_result.hpp"
#include "asio/awaitable.hpp"
#include "asio/basic_datagram_socket.hpp"
#include "asio/basic_deadline_timer.hpp"
#include "asio/basic_file.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/basic_random_access_file.hpp"
#include "asio/basic_raw_socket.hpp"
#include "asio/basic_readable_pipe.hpp"
#include "asio/basic_seq_packet_socket.hpp"
#include "asio/basic_serial_port.hpp"
#include "asio/basic_signal_set.hpp"
#include "asio/basic_socket.hpp"
#include "asio/basic_socket_acceptor.hpp"
#include "asio/basic_socket_iostream.hpp"
#include "asio/basic_socket_streambuf.hpp"
#include "asio/basic_stream_file.hpp"
#include "asio/basic_stream_socket.hpp"
#include "asio/basic_streambuf.hpp"
#include "asio/basic_waitable_timer.hpp"
#include "asio/basic_writable_pipe.hpp"
#include "asio/bind_allocator.hpp"
#include "asio/bind_cancellation_slot.hpp"
#include "asio/bind_executor.hpp"
#include "asio/bind_immediate_executor.hpp"
#include "asio/buffer.hpp"
#include "asio/buffer_registration.hpp"
#include "asio/buffered_read_stream_fwd.hpp"
#include "asio/buffered_read_stream.hpp"
#include "asio/buffered_stream_fwd.hpp"
#include "asio/buffered_stream.hpp"
#include "asio/buffered_write_stream_fwd.hpp"
#include "asio/buffered_write_stream.hpp"
#include "asio/buffers_iterator.hpp"
#include "asio/cancel_after.hpp"
#include "asio/cancel_at.hpp"
#include "asio/cancellation_signal.hpp"
#include "asio/cancellation_state.hpp"
#include "asio/cancellation_type.hpp"
#include "asio/co_composed.hpp"
#include "asio/co_spawn.hpp"
#include "asio/completion_condition.hpp"
#include "asio/compose.hpp"
#include "asio/composed.hpp"
#include "asio/connect.hpp"
#include "asio/connect_pipe.hpp"
#include "asio/consign.hpp"
#include "asio/coroutine.hpp"
#include "asio/deadline_timer.hpp"
#include "asio/defer.hpp"
#include "asio/deferred.hpp"
#include "asio/default_completion_token.hpp"
#include "asio/detached.hpp"
#include "asio/dispatch.hpp"
#include "asio/error.hpp"
#include "asio/error_code.hpp"
#include "asio/execution.hpp"
#include "asio/execution/allocator.hpp"
#include "asio/execution/any_executor.hpp"
#include "asio/execution/blocking.hpp"
#include "asio/execution/blocking_adaptation.hpp"
#include "asio/execution/context.hpp"
#include "asio/execution/context_as.hpp"
#include "asio/execution/executor.hpp"
#include "asio/execution/invocable_archetype.hpp"
#include "asio/execution/mapping.hpp"
#include "asio/execution/occupancy.hpp"
#include "asio/execution/outstanding_work.hpp"
#include "asio/execution/prefer_only.hpp"
#include "asio/execution/relationship.hpp"
#include "asio/executor.hpp"
#include "asio/executor_work_guard.hpp"
#include "asio/file_base.hpp"
#include "asio/generic/basic_endpoint.hpp"
#include "asio/generic/datagram_protocol.hpp"
#include "asio/generic/raw_protocol.hpp"
#include "asio/generic/seq_packet_protocol.hpp"
#include "asio/generic/stream_protocol.hpp"
#include "asio/handler_continuation_hook.hpp"
#include "asio/high_resolution_timer.hpp"
#include "asio/immediate.hpp"
#include "asio/io_context.hpp"
#include "asio/io_context_strand.hpp"
#include "asio/io_service.hpp"
#include "asio/io_service_strand.hpp"
#include "asio/ip/address.hpp"
#include "asio/ip/address_v4.hpp"
#include "asio/ip/address_v4_iterator.hpp"
#include "asio/ip/address_v4_range.hpp"
#include "asio/ip/address_v6.hpp"
#include "asio/ip/address_v6_iterator.hpp"
#include "asio/ip/address_v6_range.hpp"
#include "asio/ip/network_v4.hpp"
#include "asio/ip/network_v6.hpp"
#include "asio/ip/bad_address_cast.hpp"
#include "asio/ip/basic_endpoint.hpp"
#include "asio/ip/basic_resolver.hpp"
#include "asio/ip/basic_resolver_entry.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
#include "asio/ip/host_name.hpp"
#include "asio/ip/icmp.hpp"
#include "asio/ip/multicast.hpp"
#include "asio/ip/resolver_base.hpp"
#include "asio/ip/resolver_query_base.hpp"
#include "asio/ip/tcp.hpp"
#include "asio/ip/udp.hpp"
#include "asio/ip/unicast.hpp"
#include "asio/ip/v6_only.hpp"
#include "asio/is_applicable_property.hpp"
#include "asio/is_contiguous_iterator.hpp"
#include "asio/is_executor.hpp"
#include "asio/is_read_buffered.hpp"
#include "asio/is_write_buffered.hpp"
#include "asio/local/basic_endpoint.hpp"
#include "asio/local/connect_pair.hpp"
#include "asio/local/datagram_protocol.hpp"
#include "asio/local/seq_packet_protocol.hpp"
#include "asio/local/stream_protocol.hpp"
#include "asio/multiple_exceptions.hpp"
#include "asio/packaged_task.hpp"
#include "asio/placeholders.hpp"
#include "asio/posix/basic_descriptor.hpp"
#include "asio/posix/basic_stream_descriptor.hpp"
#include "asio/posix/descriptor.hpp"
#include "asio/posix/descriptor_base.hpp"
#include "asio/posix/stream_descriptor.hpp"
#include "asio/post.hpp"
#include "asio/prefer.hpp"
#include "asio/prepend.hpp"
#include "asio/query.hpp"
#include "asio/random_access_file.hpp"
#include "asio/read.hpp"
#include "asio/read_at.hpp"
#include "asio/read_until.hpp"
#include "asio/readable_pipe.hpp"
#include "asio/recycling_allocator.hpp"
#include "asio/redirect_error.hpp"
#include "asio/registered_buffer.hpp"
#include "asio/require.hpp"
#include "asio/require_concept.hpp"
#include "asio/serial_port.hpp"
#include "asio/serial_port_base.hpp"
#include "asio/signal_set.hpp"
#include "asio/signal_set_base.hpp"
#include "asio/socket_base.hpp"
#include "asio/static_thread_pool.hpp"
#include "asio/steady_timer.hpp"
#include "asio/strand.hpp"
#include "asio/stream_file.hpp"
#include "asio/streambuf.hpp"
#include "asio/system_context.hpp"
#include "asio/system_error.hpp"
#include "asio/system_executor.hpp"
#include "asio/system_timer.hpp"
#include "asio/this_coro.hpp"
#include "asio/thread.hpp"
#include "asio/thread_pool.hpp"
#include "asio/time_traits.hpp"
#include "asio/use_awaitable.hpp"
#include "asio/use_future.hpp"
#include "asio/uses_executor.hpp"
#include "asio/version.hpp"
#include "asio/wait_traits.hpp"
#include "asio/windows/basic_object_handle.hpp"
#include "asio/windows/basic_overlapped_handle.hpp"
#include "asio/windows/basic_random_access_handle.hpp"
#include "asio/windows/basic_stream_handle.hpp"
#include "asio/windows/object_handle.hpp"
#include "asio/windows/overlapped_handle.hpp"
#include "asio/windows/overlapped_ptr.hpp"
#include "asio/windows/random_access_handle.hpp"
#include "asio/windows/stream_handle.hpp"
#include "asio/writable_pipe.hpp"
#include "asio/write.hpp"
#include "asio/write_at.hpp"
#endif // ASIO_HPP

View File

@@ -1,336 +0,0 @@
//
// any_completion_executor.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ANY_COMPLETION_EXECUTOR_HPP
#define ASIO_ANY_COMPLETION_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include "asio/executor.hpp"
#else // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include "asio/execution.hpp"
#endif // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
#include "asio/detail/push_options.hpp"
namespace asio {
#if defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
typedef executor any_completion_executor;
#else // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
/// Polymorphic executor type for use with I/O objects.
/**
* The @c any_completion_executor type is a polymorphic executor that supports
* the set of properties required for the execution of completion handlers. It
* is defined as the execution::any_executor class template parameterised as
* follows:
* @code execution::any_executor<
* execution::prefer_only<execution::outstanding_work_t::tracked_t>,
* execution::prefer_only<execution::outstanding_work_t::untracked_t>
* execution::prefer_only<execution::relationship_t::fork_t>,
* execution::prefer_only<execution::relationship_t::continuation_t>
* > @endcode
*/
class any_completion_executor :
#if defined(GENERATING_DOCUMENTATION)
public execution::any_executor<...>
#else // defined(GENERATING_DOCUMENTATION)
public execution::any_executor<
execution::prefer_only<execution::outstanding_work_t::tracked_t>,
execution::prefer_only<execution::outstanding_work_t::untracked_t>,
execution::prefer_only<execution::relationship_t::fork_t>,
execution::prefer_only<execution::relationship_t::continuation_t>
>
#endif // defined(GENERATING_DOCUMENTATION)
{
public:
#if !defined(GENERATING_DOCUMENTATION)
typedef execution::any_executor<
execution::prefer_only<execution::outstanding_work_t::tracked_t>,
execution::prefer_only<execution::outstanding_work_t::untracked_t>,
execution::prefer_only<execution::relationship_t::fork_t>,
execution::prefer_only<execution::relationship_t::continuation_t>
> base_type;
typedef void supportable_properties_type(
execution::prefer_only<execution::outstanding_work_t::tracked_t>,
execution::prefer_only<execution::outstanding_work_t::untracked_t>,
execution::prefer_only<execution::relationship_t::fork_t>,
execution::prefer_only<execution::relationship_t::continuation_t>
);
#endif // !defined(GENERATING_DOCUMENTATION)
/// Default constructor.
ASIO_DECL any_completion_executor() noexcept;
/// Construct in an empty state. Equivalent effects to default constructor.
ASIO_DECL any_completion_executor(nullptr_t) noexcept;
/// Copy constructor.
ASIO_DECL any_completion_executor(
const any_completion_executor& e) noexcept;
/// Move constructor.
ASIO_DECL any_completion_executor(
any_completion_executor&& e) noexcept;
/// Construct to point to the same target as another any_executor.
#if defined(GENERATING_DOCUMENTATION)
template <class... OtherSupportableProperties>
any_completion_executor(
execution::any_executor<OtherSupportableProperties...> e);
#else // defined(GENERATING_DOCUMENTATION)
template <typename OtherAnyExecutor>
any_completion_executor(OtherAnyExecutor e,
constraint_t<
conditional<
!is_same<OtherAnyExecutor, any_completion_executor>::value
&& is_base_of<execution::detail::any_executor_base,
OtherAnyExecutor>::value,
typename execution::detail::supportable_properties<
0, supportable_properties_type>::template
is_valid_target<OtherAnyExecutor>,
false_type
>::type::value
> = 0)
: base_type(static_cast<OtherAnyExecutor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Construct to point to the same target as another any_executor.
#if defined(GENERATING_DOCUMENTATION)
template <class... OtherSupportableProperties>
any_completion_executor(std::nothrow_t,
execution::any_executor<OtherSupportableProperties...> e);
#else // defined(GENERATING_DOCUMENTATION)
template <typename OtherAnyExecutor>
any_completion_executor(std::nothrow_t, OtherAnyExecutor e,
constraint_t<
conditional<
!is_same<OtherAnyExecutor, any_completion_executor>::value
&& is_base_of<execution::detail::any_executor_base,
OtherAnyExecutor>::value,
typename execution::detail::supportable_properties<
0, supportable_properties_type>::template
is_valid_target<OtherAnyExecutor>,
false_type
>::type::value
> = 0) noexcept
: base_type(std::nothrow, static_cast<OtherAnyExecutor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Construct to point to the same target as another any_executor.
ASIO_DECL any_completion_executor(std::nothrow_t,
const any_completion_executor& e) noexcept;
/// Construct to point to the same target as another any_executor.
ASIO_DECL any_completion_executor(std::nothrow_t,
any_completion_executor&& e) noexcept;
/// Construct a polymorphic wrapper for the specified executor.
#if defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_completion_executor(Executor e);
#else // defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_completion_executor(Executor e,
constraint_t<
conditional<
!is_same<Executor, any_completion_executor>::value
&& !is_base_of<execution::detail::any_executor_base,
Executor>::value,
execution::detail::is_valid_target_executor<
Executor, supportable_properties_type>,
false_type
>::type::value
> = 0)
: base_type(static_cast<Executor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Construct a polymorphic wrapper for the specified executor.
#if defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_completion_executor(std::nothrow_t, Executor e);
#else // defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_completion_executor(std::nothrow_t, Executor e,
constraint_t<
conditional<
!is_same<Executor, any_completion_executor>::value
&& !is_base_of<execution::detail::any_executor_base,
Executor>::value,
execution::detail::is_valid_target_executor<
Executor, supportable_properties_type>,
false_type
>::type::value
> = 0) noexcept
: base_type(std::nothrow, static_cast<Executor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Assignment operator.
ASIO_DECL any_completion_executor& operator=(
const any_completion_executor& e) noexcept;
/// Move assignment operator.
ASIO_DECL any_completion_executor& operator=(
any_completion_executor&& e) noexcept;
/// Assignment operator that sets the polymorphic wrapper to the empty state.
ASIO_DECL any_completion_executor& operator=(nullptr_t);
/// Destructor.
ASIO_DECL ~any_completion_executor();
/// Swap targets with another polymorphic wrapper.
ASIO_DECL void swap(any_completion_executor& other) noexcept;
/// Obtain a polymorphic wrapper with the specified property.
/**
* Do not call this function directly. It is intended for use with the
* asio::require and asio::prefer customisation points.
*
* For example:
* @code any_completion_executor ex = ...;
* auto ex2 = asio::require(ex, execution::relationship.fork); @endcode
*/
template <typename Property>
any_completion_executor require(const Property& p,
constraint_t<
traits::require_member<const base_type&, const Property&>::is_valid
> = 0) const
{
return static_cast<const base_type&>(*this).require(p);
}
/// Obtain a polymorphic wrapper with the specified property.
/**
* Do not call this function directly. It is intended for use with the
* asio::prefer customisation point.
*
* For example:
* @code any_completion_executor ex = ...;
* auto ex2 = asio::prefer(ex, execution::relationship.fork); @endcode
*/
template <typename Property>
any_completion_executor prefer(const Property& p,
constraint_t<
traits::prefer_member<const base_type&, const Property&>::is_valid
> = 0) const
{
return static_cast<const base_type&>(*this).prefer(p);
}
};
#if !defined(GENERATING_DOCUMENTATION)
template <>
ASIO_DECL any_completion_executor any_completion_executor::prefer(
const execution::outstanding_work_t::tracked_t&, int) const;
template <>
ASIO_DECL any_completion_executor any_completion_executor::prefer(
const execution::outstanding_work_t::untracked_t&, int) const;
template <>
ASIO_DECL any_completion_executor any_completion_executor::prefer(
const execution::relationship_t::fork_t&, int) const;
template <>
ASIO_DECL any_completion_executor any_completion_executor::prefer(
const execution::relationship_t::continuation_t&, int) const;
namespace traits {
#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
template <>
struct equality_comparable<any_completion_executor>
{
static const bool is_valid = true;
static const bool is_noexcept = true;
};
#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
template <typename F>
struct execute_member<any_completion_executor, F>
{
static const bool is_valid = true;
static const bool is_noexcept = false;
typedef void result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
template <typename Prop>
struct query_member<any_completion_executor, Prop> :
query_member<any_completion_executor::base_type, Prop>
{
};
#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
template <typename Prop>
struct require_member<any_completion_executor, Prop> :
require_member<any_completion_executor::base_type, Prop>
{
typedef any_completion_executor result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
template <typename Prop>
struct prefer_member<any_completion_executor, Prop> :
prefer_member<any_completion_executor::base_type, Prop>
{
typedef any_completion_executor result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
} // namespace traits
#endif // !defined(GENERATING_DOCUMENTATION)
#endif // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#if defined(ASIO_HEADER_ONLY) \
&& !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include "asio/impl/any_completion_executor.ipp"
#endif // defined(ASIO_HEADER_ONLY)
// && !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
#endif // ASIO_ANY_COMPLETION_EXECUTOR_HPP

View File

@@ -1,822 +0,0 @@
//
// any_completion_handler.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ANY_COMPLETION_HANDLER_HPP
#define ASIO_ANY_COMPLETION_HANDLER_HPP
#include "asio/detail/config.hpp"
#include <cstring>
#include <functional>
#include <memory>
#include <utility>
#include "asio/any_completion_executor.hpp"
#include "asio/any_io_executor.hpp"
#include "asio/associated_allocator.hpp"
#include "asio/associated_cancellation_slot.hpp"
#include "asio/associated_executor.hpp"
#include "asio/associated_immediate_executor.hpp"
#include "asio/cancellation_state.hpp"
#include "asio/recycling_allocator.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
class any_completion_handler_impl_base
{
public:
template <typename S>
explicit any_completion_handler_impl_base(S&& slot)
: cancel_state_(static_cast<S&&>(slot), enable_total_cancellation())
{
}
cancellation_slot get_cancellation_slot() const noexcept
{
return cancel_state_.slot();
}
private:
cancellation_state cancel_state_;
};
template <typename Handler>
class any_completion_handler_impl :
public any_completion_handler_impl_base
{
public:
template <typename S, typename H>
any_completion_handler_impl(S&& slot, H&& h)
: any_completion_handler_impl_base(static_cast<S&&>(slot)),
handler_(static_cast<H&&>(h))
{
}
struct uninit_deleter
{
typename std::allocator_traits<
associated_allocator_t<Handler,
asio::recycling_allocator<void>>>::template
rebind_alloc<any_completion_handler_impl> alloc;
void operator()(any_completion_handler_impl* ptr)
{
std::allocator_traits<decltype(alloc)>::deallocate(alloc, ptr, 1);
}
};
struct deleter
{
typename std::allocator_traits<
associated_allocator_t<Handler,
asio::recycling_allocator<void>>>::template
rebind_alloc<any_completion_handler_impl> alloc;
void operator()(any_completion_handler_impl* ptr)
{
std::allocator_traits<decltype(alloc)>::destroy(alloc, ptr);
std::allocator_traits<decltype(alloc)>::deallocate(alloc, ptr, 1);
}
};
template <typename S, typename H>
static any_completion_handler_impl* create(S&& slot, H&& h)
{
uninit_deleter d{
(get_associated_allocator)(h,
asio::recycling_allocator<void>())};
std::unique_ptr<any_completion_handler_impl, uninit_deleter> uninit_ptr(
std::allocator_traits<decltype(d.alloc)>::allocate(d.alloc, 1), d);
any_completion_handler_impl* ptr =
new (uninit_ptr.get()) any_completion_handler_impl(
static_cast<S&&>(slot), static_cast<H&&>(h));
uninit_ptr.release();
return ptr;
}
void destroy()
{
deleter d{
(get_associated_allocator)(handler_,
asio::recycling_allocator<void>())};
d(this);
}
any_completion_executor executor(
const any_completion_executor& candidate) const noexcept
{
return any_completion_executor(std::nothrow,
(get_associated_executor)(handler_, candidate));
}
any_completion_executor immediate_executor(
const any_io_executor& candidate) const noexcept
{
return any_completion_executor(std::nothrow,
(get_associated_immediate_executor)(handler_, candidate));
}
void* allocate(std::size_t size, std::size_t align) const
{
typename std::allocator_traits<
associated_allocator_t<Handler,
asio::recycling_allocator<void>>>::template
rebind_alloc<unsigned char> alloc(
(get_associated_allocator)(handler_,
asio::recycling_allocator<void>()));
std::size_t space = size + align - 1;
unsigned char* base =
std::allocator_traits<decltype(alloc)>::allocate(
alloc, space + sizeof(std::ptrdiff_t));
void* p = base;
if (detail::align(align, size, p, space))
{
std::ptrdiff_t off = static_cast<unsigned char*>(p) - base;
std::memcpy(static_cast<unsigned char*>(p) + size, &off, sizeof(off));
return p;
}
std::bad_alloc ex;
asio::detail::throw_exception(ex);
return nullptr;
}
void deallocate(void* p, std::size_t size, std::size_t align) const
{
if (p)
{
typename std::allocator_traits<
associated_allocator_t<Handler,
asio::recycling_allocator<void>>>::template
rebind_alloc<unsigned char> alloc(
(get_associated_allocator)(handler_,
asio::recycling_allocator<void>()));
std::ptrdiff_t off;
std::memcpy(&off, static_cast<unsigned char*>(p) + size, sizeof(off));
unsigned char* base = static_cast<unsigned char*>(p) - off;
std::allocator_traits<decltype(alloc)>::deallocate(
alloc, base, size + align -1 + sizeof(std::ptrdiff_t));
}
}
template <typename... Args>
void call(Args&&... args)
{
deleter d{
(get_associated_allocator)(handler_,
asio::recycling_allocator<void>())};
std::unique_ptr<any_completion_handler_impl, deleter> ptr(this, d);
Handler handler(static_cast<Handler&&>(handler_));
ptr.reset();
static_cast<Handler&&>(handler)(
static_cast<Args&&>(args)...);
}
private:
Handler handler_;
};
template <typename Signature>
class any_completion_handler_call_fn;
template <typename R, typename... Args>
class any_completion_handler_call_fn<R(Args...)>
{
public:
using type = void(*)(any_completion_handler_impl_base*, Args...);
constexpr any_completion_handler_call_fn(type fn)
: call_fn_(fn)
{
}
void call(any_completion_handler_impl_base* impl, Args... args) const
{
call_fn_(impl, static_cast<Args&&>(args)...);
}
template <typename Handler>
static void impl(any_completion_handler_impl_base* impl, Args... args)
{
static_cast<any_completion_handler_impl<Handler>*>(impl)->call(
static_cast<Args&&>(args)...);
}
private:
type call_fn_;
};
template <typename... Signatures>
class any_completion_handler_call_fns;
template <typename Signature>
class any_completion_handler_call_fns<Signature> :
public any_completion_handler_call_fn<Signature>
{
public:
using any_completion_handler_call_fn<
Signature>::any_completion_handler_call_fn;
using any_completion_handler_call_fn<Signature>::call;
};
template <typename Signature, typename... Signatures>
class any_completion_handler_call_fns<Signature, Signatures...> :
public any_completion_handler_call_fn<Signature>,
public any_completion_handler_call_fns<Signatures...>
{
public:
template <typename CallFn, typename... CallFns>
constexpr any_completion_handler_call_fns(CallFn fn, CallFns... fns)
: any_completion_handler_call_fn<Signature>(fn),
any_completion_handler_call_fns<Signatures...>(fns...)
{
}
using any_completion_handler_call_fn<Signature>::call;
using any_completion_handler_call_fns<Signatures...>::call;
};
class any_completion_handler_destroy_fn
{
public:
using type = void(*)(any_completion_handler_impl_base*);
constexpr any_completion_handler_destroy_fn(type fn)
: destroy_fn_(fn)
{
}
void destroy(any_completion_handler_impl_base* impl) const
{
destroy_fn_(impl);
}
template <typename Handler>
static void impl(any_completion_handler_impl_base* impl)
{
static_cast<any_completion_handler_impl<Handler>*>(impl)->destroy();
}
private:
type destroy_fn_;
};
class any_completion_handler_executor_fn
{
public:
using type = any_completion_executor(*)(
any_completion_handler_impl_base*, const any_completion_executor&);
constexpr any_completion_handler_executor_fn(type fn)
: executor_fn_(fn)
{
}
any_completion_executor executor(any_completion_handler_impl_base* impl,
const any_completion_executor& candidate) const
{
return executor_fn_(impl, candidate);
}
template <typename Handler>
static any_completion_executor impl(any_completion_handler_impl_base* impl,
const any_completion_executor& candidate)
{
return static_cast<any_completion_handler_impl<Handler>*>(impl)->executor(
candidate);
}
private:
type executor_fn_;
};
class any_completion_handler_immediate_executor_fn
{
public:
using type = any_completion_executor(*)(
any_completion_handler_impl_base*, const any_io_executor&);
constexpr any_completion_handler_immediate_executor_fn(type fn)
: immediate_executor_fn_(fn)
{
}
any_completion_executor immediate_executor(
any_completion_handler_impl_base* impl,
const any_io_executor& candidate) const
{
return immediate_executor_fn_(impl, candidate);
}
template <typename Handler>
static any_completion_executor impl(any_completion_handler_impl_base* impl,
const any_io_executor& candidate)
{
return static_cast<any_completion_handler_impl<Handler>*>(
impl)->immediate_executor(candidate);
}
private:
type immediate_executor_fn_;
};
class any_completion_handler_allocate_fn
{
public:
using type = void*(*)(any_completion_handler_impl_base*,
std::size_t, std::size_t);
constexpr any_completion_handler_allocate_fn(type fn)
: allocate_fn_(fn)
{
}
void* allocate(any_completion_handler_impl_base* impl,
std::size_t size, std::size_t align) const
{
return allocate_fn_(impl, size, align);
}
template <typename Handler>
static void* impl(any_completion_handler_impl_base* impl,
std::size_t size, std::size_t align)
{
return static_cast<any_completion_handler_impl<Handler>*>(impl)->allocate(
size, align);
}
private:
type allocate_fn_;
};
class any_completion_handler_deallocate_fn
{
public:
using type = void(*)(any_completion_handler_impl_base*,
void*, std::size_t, std::size_t);
constexpr any_completion_handler_deallocate_fn(type fn)
: deallocate_fn_(fn)
{
}
void deallocate(any_completion_handler_impl_base* impl,
void* p, std::size_t size, std::size_t align) const
{
deallocate_fn_(impl, p, size, align);
}
template <typename Handler>
static void impl(any_completion_handler_impl_base* impl,
void* p, std::size_t size, std::size_t align)
{
static_cast<any_completion_handler_impl<Handler>*>(impl)->deallocate(
p, size, align);
}
private:
type deallocate_fn_;
};
template <typename... Signatures>
class any_completion_handler_fn_table
: private any_completion_handler_destroy_fn,
private any_completion_handler_executor_fn,
private any_completion_handler_immediate_executor_fn,
private any_completion_handler_allocate_fn,
private any_completion_handler_deallocate_fn,
private any_completion_handler_call_fns<Signatures...>
{
public:
template <typename... CallFns>
constexpr any_completion_handler_fn_table(
any_completion_handler_destroy_fn::type destroy_fn,
any_completion_handler_executor_fn::type executor_fn,
any_completion_handler_immediate_executor_fn::type immediate_executor_fn,
any_completion_handler_allocate_fn::type allocate_fn,
any_completion_handler_deallocate_fn::type deallocate_fn,
CallFns... call_fns)
: any_completion_handler_destroy_fn(destroy_fn),
any_completion_handler_executor_fn(executor_fn),
any_completion_handler_immediate_executor_fn(immediate_executor_fn),
any_completion_handler_allocate_fn(allocate_fn),
any_completion_handler_deallocate_fn(deallocate_fn),
any_completion_handler_call_fns<Signatures...>(call_fns...)
{
}
using any_completion_handler_destroy_fn::destroy;
using any_completion_handler_executor_fn::executor;
using any_completion_handler_immediate_executor_fn::immediate_executor;
using any_completion_handler_allocate_fn::allocate;
using any_completion_handler_deallocate_fn::deallocate;
using any_completion_handler_call_fns<Signatures...>::call;
};
template <typename Handler, typename... Signatures>
struct any_completion_handler_fn_table_instance
{
static constexpr any_completion_handler_fn_table<Signatures...>
value = any_completion_handler_fn_table<Signatures...>(
&any_completion_handler_destroy_fn::impl<Handler>,
&any_completion_handler_executor_fn::impl<Handler>,
&any_completion_handler_immediate_executor_fn::impl<Handler>,
&any_completion_handler_allocate_fn::impl<Handler>,
&any_completion_handler_deallocate_fn::impl<Handler>,
&any_completion_handler_call_fn<Signatures>::template impl<Handler>...);
};
template <typename Handler, typename... Signatures>
constexpr any_completion_handler_fn_table<Signatures...>
any_completion_handler_fn_table_instance<Handler, Signatures...>::value;
} // namespace detail
template <typename... Signatures>
class any_completion_handler;
/// An allocator type that forwards memory allocation operations through an
/// instance of @c any_completion_handler.
template <typename T, typename... Signatures>
class any_completion_handler_allocator
{
private:
template <typename...>
friend class any_completion_handler;
template <typename, typename...>
friend class any_completion_handler_allocator;
const detail::any_completion_handler_fn_table<Signatures...>* fn_table_;
detail::any_completion_handler_impl_base* impl_;
constexpr any_completion_handler_allocator(int,
const any_completion_handler<Signatures...>& h) noexcept
: fn_table_(h.fn_table_),
impl_(h.impl_)
{
}
public:
/// The type of objects that may be allocated by the allocator.
typedef T value_type;
/// Rebinds an allocator to another value type.
template <typename U>
struct rebind
{
/// Specifies the type of the rebound allocator.
typedef any_completion_handler_allocator<U, Signatures...> other;
};
/// Construct from another @c any_completion_handler_allocator.
template <typename U>
constexpr any_completion_handler_allocator(
const any_completion_handler_allocator<U, Signatures...>& a)
noexcept
: fn_table_(a.fn_table_),
impl_(a.impl_)
{
}
/// Equality operator.
constexpr bool operator==(
const any_completion_handler_allocator& other) const noexcept
{
return fn_table_ == other.fn_table_ && impl_ == other.impl_;
}
/// Inequality operator.
constexpr bool operator!=(
const any_completion_handler_allocator& other) const noexcept
{
return fn_table_ != other.fn_table_ || impl_ != other.impl_;
}
/// Allocate space for @c n objects of the allocator's value type.
T* allocate(std::size_t n) const
{
if (fn_table_)
{
return static_cast<T*>(
fn_table_->allocate(
impl_, sizeof(T) * n, alignof(T)));
}
std::bad_alloc ex;
asio::detail::throw_exception(ex);
return nullptr;
}
/// Deallocate space for @c n objects of the allocator's value type.
void deallocate(T* p, std::size_t n) const
{
fn_table_->deallocate(impl_, p, sizeof(T) * n, alignof(T));
}
};
/// A protoco-allocator type that may be rebound to obtain an allocator that
/// forwards memory allocation operations through an instance of
/// @c any_completion_handler.
template <typename... Signatures>
class any_completion_handler_allocator<void, Signatures...>
{
private:
template <typename...>
friend class any_completion_handler;
template <typename, typename...>
friend class any_completion_handler_allocator;
const detail::any_completion_handler_fn_table<Signatures...>* fn_table_;
detail::any_completion_handler_impl_base* impl_;
constexpr any_completion_handler_allocator(int,
const any_completion_handler<Signatures...>& h) noexcept
: fn_table_(h.fn_table_),
impl_(h.impl_)
{
}
public:
/// @c void as no objects can be allocated through a proto-allocator.
typedef void value_type;
/// Rebinds an allocator to another value type.
template <typename U>
struct rebind
{
/// Specifies the type of the rebound allocator.
typedef any_completion_handler_allocator<U, Signatures...> other;
};
/// Construct from another @c any_completion_handler_allocator.
template <typename U>
constexpr any_completion_handler_allocator(
const any_completion_handler_allocator<U, Signatures...>& a)
noexcept
: fn_table_(a.fn_table_),
impl_(a.impl_)
{
}
/// Equality operator.
constexpr bool operator==(
const any_completion_handler_allocator& other) const noexcept
{
return fn_table_ == other.fn_table_ && impl_ == other.impl_;
}
/// Inequality operator.
constexpr bool operator!=(
const any_completion_handler_allocator& other) const noexcept
{
return fn_table_ != other.fn_table_ || impl_ != other.impl_;
}
};
/// Polymorphic wrapper for completion handlers.
/**
* The @c any_completion_handler class template is a polymorphic wrapper for
* completion handlers that propagates the associated executor, associated
* allocator, and associated cancellation slot through a type-erasing interface.
*
* When using @c any_completion_handler, specify one or more completion
* signatures as template parameters. These will dictate the arguments that may
* be passed to the handler through the polymorphic interface.
*
* Typical uses for @c any_completion_handler include:
*
* @li Separate compilation of asynchronous operation implementations.
*
* @li Enabling interoperability between asynchronous operations and virtual
* functions.
*/
template <typename... Signatures>
class any_completion_handler
{
#if !defined(GENERATING_DOCUMENTATION)
private:
template <typename, typename...>
friend class any_completion_handler_allocator;
template <typename, typename>
friend struct associated_executor;
template <typename, typename>
friend struct associated_immediate_executor;
const detail::any_completion_handler_fn_table<Signatures...>* fn_table_;
detail::any_completion_handler_impl_base* impl_;
#endif // !defined(GENERATING_DOCUMENTATION)
public:
/// The associated allocator type.
using allocator_type = any_completion_handler_allocator<void, Signatures...>;
/// The associated cancellation slot type.
using cancellation_slot_type = cancellation_slot;
/// Construct an @c any_completion_handler in an empty state, without a target
/// object.
constexpr any_completion_handler()
: fn_table_(nullptr),
impl_(nullptr)
{
}
/// Construct an @c any_completion_handler in an empty state, without a target
/// object.
constexpr any_completion_handler(nullptr_t)
: fn_table_(nullptr),
impl_(nullptr)
{
}
/// Construct an @c any_completion_handler to contain the specified target.
template <typename H, typename Handler = decay_t<H>>
any_completion_handler(H&& h,
constraint_t<
!is_same<decay_t<H>, any_completion_handler>::value
> = 0)
: fn_table_(
&detail::any_completion_handler_fn_table_instance<
Handler, Signatures...>::value),
impl_(detail::any_completion_handler_impl<Handler>::create(
(get_associated_cancellation_slot)(h), static_cast<H&&>(h)))
{
}
/// Move-construct an @c any_completion_handler from another.
/**
* After the operation, the moved-from object @c other has no target.
*/
any_completion_handler(any_completion_handler&& other) noexcept
: fn_table_(other.fn_table_),
impl_(other.impl_)
{
other.fn_table_ = nullptr;
other.impl_ = nullptr;
}
/// Move-assign an @c any_completion_handler from another.
/**
* After the operation, the moved-from object @c other has no target.
*/
any_completion_handler& operator=(
any_completion_handler&& other) noexcept
{
any_completion_handler(
static_cast<any_completion_handler&&>(other)).swap(*this);
return *this;
}
/// Assignment operator that sets the polymorphic wrapper to the empty state.
any_completion_handler& operator=(nullptr_t) noexcept
{
any_completion_handler().swap(*this);
return *this;
}
/// Destructor.
~any_completion_handler()
{
if (impl_)
fn_table_->destroy(impl_);
}
/// Test if the polymorphic wrapper is empty.
constexpr explicit operator bool() const noexcept
{
return impl_ != nullptr;
}
/// Test if the polymorphic wrapper is non-empty.
constexpr bool operator!() const noexcept
{
return impl_ == nullptr;
}
/// Swap the content of an @c any_completion_handler with another.
void swap(any_completion_handler& other) noexcept
{
std::swap(fn_table_, other.fn_table_);
std::swap(impl_, other.impl_);
}
/// Get the associated allocator.
allocator_type get_allocator() const noexcept
{
return allocator_type(0, *this);
}
/// Get the associated cancellation slot.
cancellation_slot_type get_cancellation_slot() const noexcept
{
return impl_ ? impl_->get_cancellation_slot() : cancellation_slot_type();
}
/// Function call operator.
/**
* Invokes target completion handler with the supplied arguments.
*
* This function may only be called once, as the target handler is moved from.
* The polymorphic wrapper is left in an empty state.
*
* Throws @c std::bad_function_call if the polymorphic wrapper is empty.
*/
template <typename... Args>
auto operator()(Args&&... args)
-> decltype(fn_table_->call(impl_, static_cast<Args&&>(args)...))
{
if (detail::any_completion_handler_impl_base* impl = impl_)
{
impl_ = nullptr;
return fn_table_->call(impl, static_cast<Args&&>(args)...);
}
std::bad_function_call ex;
asio::detail::throw_exception(ex);
}
/// Equality operator.
friend constexpr bool operator==(
const any_completion_handler& a, nullptr_t) noexcept
{
return a.impl_ == nullptr;
}
/// Equality operator.
friend constexpr bool operator==(
nullptr_t, const any_completion_handler& b) noexcept
{
return nullptr == b.impl_;
}
/// Inequality operator.
friend constexpr bool operator!=(
const any_completion_handler& a, nullptr_t) noexcept
{
return a.impl_ != nullptr;
}
/// Inequality operator.
friend constexpr bool operator!=(
nullptr_t, const any_completion_handler& b) noexcept
{
return nullptr != b.impl_;
}
};
template <typename... Signatures, typename Candidate>
struct associated_executor<any_completion_handler<Signatures...>, Candidate>
{
using type = any_completion_executor;
static type get(const any_completion_handler<Signatures...>& handler,
const Candidate& candidate = Candidate()) noexcept
{
any_completion_executor any_candidate(std::nothrow, candidate);
return handler.fn_table_
? handler.fn_table_->executor(handler.impl_, any_candidate)
: any_candidate;
}
};
template <typename... Signatures, typename Candidate>
struct associated_immediate_executor<
any_completion_handler<Signatures...>, Candidate>
{
using type = any_completion_executor;
static type get(const any_completion_handler<Signatures...>& handler,
const Candidate& candidate = Candidate()) noexcept
{
any_io_executor any_candidate(std::nothrow, candidate);
return handler.fn_table_
? handler.fn_table_->immediate_executor(handler.impl_, any_candidate)
: any_candidate;
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_ANY_COMPLETION_HANDLER_HPP

View File

@@ -1,351 +0,0 @@
//
// any_io_executor.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ANY_IO_EXECUTOR_HPP
#define ASIO_ANY_IO_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include "asio/executor.hpp"
#else // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include "asio/execution.hpp"
# include "asio/execution_context.hpp"
#endif // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
#include "asio/detail/push_options.hpp"
namespace asio {
#if defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
typedef executor any_io_executor;
#else // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
/// Polymorphic executor type for use with I/O objects.
/**
* The @c any_io_executor type is a polymorphic executor that supports the set
* of properties required by I/O objects. It is defined as the
* execution::any_executor class template parameterised as follows:
* @code execution::any_executor<
* execution::context_as_t<execution_context&>,
* execution::blocking_t::never_t,
* execution::prefer_only<execution::blocking_t::possibly_t>,
* execution::prefer_only<execution::outstanding_work_t::tracked_t>,
* execution::prefer_only<execution::outstanding_work_t::untracked_t>,
* execution::prefer_only<execution::relationship_t::fork_t>,
* execution::prefer_only<execution::relationship_t::continuation_t>
* > @endcode
*/
class any_io_executor :
#if defined(GENERATING_DOCUMENTATION)
public execution::any_executor<...>
#else // defined(GENERATING_DOCUMENTATION)
public execution::any_executor<
execution::context_as_t<execution_context&>,
execution::blocking_t::never_t,
execution::prefer_only<execution::blocking_t::possibly_t>,
execution::prefer_only<execution::outstanding_work_t::tracked_t>,
execution::prefer_only<execution::outstanding_work_t::untracked_t>,
execution::prefer_only<execution::relationship_t::fork_t>,
execution::prefer_only<execution::relationship_t::continuation_t>
>
#endif // defined(GENERATING_DOCUMENTATION)
{
public:
#if !defined(GENERATING_DOCUMENTATION)
typedef execution::any_executor<
execution::context_as_t<execution_context&>,
execution::blocking_t::never_t,
execution::prefer_only<execution::blocking_t::possibly_t>,
execution::prefer_only<execution::outstanding_work_t::tracked_t>,
execution::prefer_only<execution::outstanding_work_t::untracked_t>,
execution::prefer_only<execution::relationship_t::fork_t>,
execution::prefer_only<execution::relationship_t::continuation_t>
> base_type;
typedef void supportable_properties_type(
execution::context_as_t<execution_context&>,
execution::blocking_t::never_t,
execution::prefer_only<execution::blocking_t::possibly_t>,
execution::prefer_only<execution::outstanding_work_t::tracked_t>,
execution::prefer_only<execution::outstanding_work_t::untracked_t>,
execution::prefer_only<execution::relationship_t::fork_t>,
execution::prefer_only<execution::relationship_t::continuation_t>
);
#endif // !defined(GENERATING_DOCUMENTATION)
/// Default constructor.
ASIO_DECL any_io_executor() noexcept;
/// Construct in an empty state. Equivalent effects to default constructor.
ASIO_DECL any_io_executor(nullptr_t) noexcept;
/// Copy constructor.
ASIO_DECL any_io_executor(const any_io_executor& e) noexcept;
/// Move constructor.
ASIO_DECL any_io_executor(any_io_executor&& e) noexcept;
/// Construct to point to the same target as another any_executor.
#if defined(GENERATING_DOCUMENTATION)
template <class... OtherSupportableProperties>
any_io_executor(execution::any_executor<OtherSupportableProperties...> e);
#else // defined(GENERATING_DOCUMENTATION)
template <typename OtherAnyExecutor>
any_io_executor(OtherAnyExecutor e,
constraint_t<
conditional_t<
!is_same<OtherAnyExecutor, any_io_executor>::value
&& is_base_of<execution::detail::any_executor_base,
OtherAnyExecutor>::value,
typename execution::detail::supportable_properties<
0, supportable_properties_type>::template
is_valid_target<OtherAnyExecutor>,
false_type
>::value
> = 0)
: base_type(static_cast<OtherAnyExecutor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Construct to point to the same target as another any_executor.
#if defined(GENERATING_DOCUMENTATION)
template <class... OtherSupportableProperties>
any_io_executor(std::nothrow_t,
execution::any_executor<OtherSupportableProperties...> e);
#else // defined(GENERATING_DOCUMENTATION)
template <typename OtherAnyExecutor>
any_io_executor(std::nothrow_t, OtherAnyExecutor e,
constraint_t<
conditional_t<
!is_same<OtherAnyExecutor, any_io_executor>::value
&& is_base_of<execution::detail::any_executor_base,
OtherAnyExecutor>::value,
typename execution::detail::supportable_properties<
0, supportable_properties_type>::template
is_valid_target<OtherAnyExecutor>,
false_type
>::value
> = 0) noexcept
: base_type(std::nothrow, static_cast<OtherAnyExecutor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Construct to point to the same target as another any_executor.
ASIO_DECL any_io_executor(std::nothrow_t,
const any_io_executor& e) noexcept;
/// Construct to point to the same target as another any_executor.
ASIO_DECL any_io_executor(std::nothrow_t, any_io_executor&& e) noexcept;
/// Construct a polymorphic wrapper for the specified executor.
#if defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_io_executor(Executor e);
#else // defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_io_executor(Executor e,
constraint_t<
conditional_t<
!is_same<Executor, any_io_executor>::value
&& !is_base_of<execution::detail::any_executor_base,
Executor>::value,
execution::detail::is_valid_target_executor<
Executor, supportable_properties_type>,
false_type
>::value
> = 0)
: base_type(static_cast<Executor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Construct a polymorphic wrapper for the specified executor.
#if defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_io_executor(std::nothrow_t, Executor e);
#else // defined(GENERATING_DOCUMENTATION)
template <ASIO_EXECUTION_EXECUTOR Executor>
any_io_executor(std::nothrow_t, Executor e,
constraint_t<
conditional_t<
!is_same<Executor, any_io_executor>::value
&& !is_base_of<execution::detail::any_executor_base,
Executor>::value,
execution::detail::is_valid_target_executor<
Executor, supportable_properties_type>,
false_type
>::value
> = 0) noexcept
: base_type(std::nothrow, static_cast<Executor&&>(e))
{
}
#endif // defined(GENERATING_DOCUMENTATION)
/// Assignment operator.
ASIO_DECL any_io_executor& operator=(
const any_io_executor& e) noexcept;
/// Move assignment operator.
ASIO_DECL any_io_executor& operator=(any_io_executor&& e) noexcept;
/// Assignment operator that sets the polymorphic wrapper to the empty state.
ASIO_DECL any_io_executor& operator=(nullptr_t);
/// Destructor.
ASIO_DECL ~any_io_executor();
/// Swap targets with another polymorphic wrapper.
ASIO_DECL void swap(any_io_executor& other) noexcept;
/// Obtain a polymorphic wrapper with the specified property.
/**
* Do not call this function directly. It is intended for use with the
* asio::require and asio::prefer customisation points.
*
* For example:
* @code any_io_executor ex = ...;
* auto ex2 = asio::require(ex, execution::blocking.possibly); @endcode
*/
template <typename Property>
any_io_executor require(const Property& p,
constraint_t<
traits::require_member<const base_type&, const Property&>::is_valid
> = 0) const
{
return static_cast<const base_type&>(*this).require(p);
}
/// Obtain a polymorphic wrapper with the specified property.
/**
* Do not call this function directly. It is intended for use with the
* asio::prefer customisation point.
*
* For example:
* @code any_io_executor ex = ...;
* auto ex2 = asio::prefer(ex, execution::blocking.possibly); @endcode
*/
template <typename Property>
any_io_executor prefer(const Property& p,
constraint_t<
traits::prefer_member<const base_type&, const Property&>::is_valid
> = 0) const
{
return static_cast<const base_type&>(*this).prefer(p);
}
};
#if !defined(GENERATING_DOCUMENTATION)
template <>
ASIO_DECL any_io_executor any_io_executor::require(
const execution::blocking_t::never_t&, int) const;
template <>
ASIO_DECL any_io_executor any_io_executor::prefer(
const execution::blocking_t::possibly_t&, int) const;
template <>
ASIO_DECL any_io_executor any_io_executor::prefer(
const execution::outstanding_work_t::tracked_t&, int) const;
template <>
ASIO_DECL any_io_executor any_io_executor::prefer(
const execution::outstanding_work_t::untracked_t&, int) const;
template <>
ASIO_DECL any_io_executor any_io_executor::prefer(
const execution::relationship_t::fork_t&, int) const;
template <>
ASIO_DECL any_io_executor any_io_executor::prefer(
const execution::relationship_t::continuation_t&, int) const;
namespace traits {
#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
template <>
struct equality_comparable<any_io_executor>
{
static const bool is_valid = true;
static const bool is_noexcept = true;
};
#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
template <typename F>
struct execute_member<any_io_executor, F>
{
static const bool is_valid = true;
static const bool is_noexcept = false;
typedef void result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
template <typename Prop>
struct query_member<any_io_executor, Prop> :
query_member<any_io_executor::base_type, Prop>
{
};
#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
template <typename Prop>
struct require_member<any_io_executor, Prop> :
require_member<any_io_executor::base_type, Prop>
{
typedef any_io_executor result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
template <typename Prop>
struct prefer_member<any_io_executor, Prop> :
prefer_member<any_io_executor::base_type, Prop>
{
typedef any_io_executor result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
} // namespace traits
#endif // !defined(GENERATING_DOCUMENTATION)
#endif // defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#if defined(ASIO_HEADER_ONLY) \
&& !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
# include "asio/impl/any_io_executor.ipp"
#endif // defined(ASIO_HEADER_ONLY)
// && !defined(ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
#endif // ASIO_ANY_IO_EXECUTOR_HPP

View File

@@ -1,65 +0,0 @@
//
// append.hpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_APPEND_HPP
#define ASIO_APPEND_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <tuple>
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
/// Completion token type used to specify that the completion handler
/// arguments should be passed additional values after the results of the
/// operation.
template <typename CompletionToken, typename... Values>
class append_t
{
public:
/// Constructor.
template <typename T, typename... V>
constexpr explicit append_t(T&& completion_token, V&&... values)
: token_(static_cast<T&&>(completion_token)),
values_(static_cast<V&&>(values)...)
{
}
//private:
CompletionToken token_;
std::tuple<Values...> values_;
};
/// Completion token type used to specify that the completion handler
/// arguments should be passed additional values after the results of the
/// operation.
template <typename CompletionToken, typename... Values>
ASIO_NODISCARD inline constexpr
append_t<decay_t<CompletionToken>, decay_t<Values>...>
append(CompletionToken&& completion_token, Values&&... values)
{
return append_t<decay_t<CompletionToken>, decay_t<Values>...>(
static_cast<CompletionToken&&>(completion_token),
static_cast<Values&&>(values)...);
}
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/impl/append.hpp"
#endif // ASIO_APPEND_HPP

View File

@@ -1,152 +0,0 @@
//
// as_tuple.hpp
// ~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_AS_TUPLE_HPP
#define ASIO_AS_TUPLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
/// A @ref completion_token adapter used to specify that the completion handler
/// arguments should be combined into a single tuple argument.
/**
* The as_tuple_t class is used to indicate that any arguments to the
* completion handler should be combined and passed as a single tuple argument.
* The arguments are first moved into a @c std::tuple and that tuple is then
* passed to the completion handler.
*/
template <typename CompletionToken>
class as_tuple_t
{
public:
/// Tag type used to prevent the "default" constructor from being used for
/// conversions.
struct default_constructor_tag {};
/// Default constructor.
/**
* This constructor is only valid if the underlying completion token is
* default constructible and move constructible. The underlying completion
* token is itself defaulted as an argument to allow it to capture a source
* location.
*/
constexpr as_tuple_t(
default_constructor_tag = default_constructor_tag(),
CompletionToken token = CompletionToken())
: token_(static_cast<CompletionToken&&>(token))
{
}
/// Constructor.
template <typename T>
constexpr explicit as_tuple_t(
T&& completion_token)
: token_(static_cast<T&&>(completion_token))
{
}
/// Adapts an executor to add the @c as_tuple_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c as_tuple_t as the default completion token type.
typedef as_tuple_t default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
template <typename InnerExecutor1>
executor_with_default(const InnerExecutor1& ex,
constraint_t<
conditional_t<
!is_same<InnerExecutor1, executor_with_default>::value,
is_convertible<InnerExecutor1, InnerExecutor>,
false_type
>::value
> = 0) noexcept
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c as_tuple_t as its
/// default completion token type.
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type>>::other;
/// Function helper to adapt an I/O object to use @c as_tuple_t as its
/// default completion token type.
template <typename T>
static typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other
as_default_on(T&& object)
{
return typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other(static_cast<T&&>(object));
}
//private:
CompletionToken token_;
};
/// A function object type that adapts a @ref completion_token to specify that
/// the completion handler arguments should be combined into a single tuple
/// argument.
/**
* May also be used directly as a completion token, in which case it adapts the
* asynchronous operation's default completion token (or asio::deferred
* if no default is available).
*/
struct partial_as_tuple
{
/// Default constructor.
constexpr partial_as_tuple()
{
}
/// Adapt a @ref completion_token to specify that the completion handler
/// arguments should be combined into a single tuple argument.
template <typename CompletionToken>
ASIO_NODISCARD inline
constexpr as_tuple_t<decay_t<CompletionToken>>
operator()(CompletionToken&& completion_token) const
{
return as_tuple_t<decay_t<CompletionToken>>(
static_cast<CompletionToken&&>(completion_token));
}
};
/// A function object that adapts a @ref completion_token to specify that the
/// completion handler arguments should be combined into a single tuple
/// argument.
/**
* May also be used directly as a completion token, in which case it adapts the
* asynchronous operation's default completion token (or asio::deferred
* if no default is available).
*/
constexpr partial_as_tuple as_tuple;
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/impl/as_tuple.hpp"
#endif // ASIO_AS_TUPLE_HPP

View File

@@ -1,214 +0,0 @@
//
// associated_allocator.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ASSOCIATED_ALLOCATOR_HPP
#define ASIO_ASSOCIATED_ALLOCATOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <memory>
#include "asio/associator.hpp"
#include "asio/detail/functional.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
template <typename T, typename Allocator>
struct associated_allocator;
namespace detail {
template <typename T, typename = void>
struct has_allocator_type : false_type
{
};
template <typename T>
struct has_allocator_type<T, void_t<typename T::allocator_type>> : true_type
{
};
template <typename T, typename A, typename = void, typename = void>
struct associated_allocator_impl
{
typedef void asio_associated_allocator_is_unspecialised;
typedef A type;
static type get(const T&) noexcept
{
return type();
}
static const type& get(const T&, const A& a) noexcept
{
return a;
}
};
template <typename T, typename A>
struct associated_allocator_impl<T, A, void_t<typename T::allocator_type>>
{
typedef typename T::allocator_type type;
static auto get(const T& t) noexcept
-> decltype(t.get_allocator())
{
return t.get_allocator();
}
static auto get(const T& t, const A&) noexcept
-> decltype(t.get_allocator())
{
return t.get_allocator();
}
};
template <typename T, typename A>
struct associated_allocator_impl<T, A,
enable_if_t<
!has_allocator_type<T>::value
>,
void_t<
typename associator<associated_allocator, T, A>::type
>> : associator<associated_allocator, T, A>
{
};
} // namespace detail
/// Traits type used to obtain the allocator associated with an object.
/**
* A program may specialise this traits type if the @c T template parameter in
* the specialisation is a user-defined type. The template parameter @c
* Allocator shall be a type meeting the Allocator requirements.
*
* Specialisations shall meet the following requirements, where @c t is a const
* reference to an object of type @c T, and @c a is an object of type @c
* Allocator.
*
* @li Provide a nested typedef @c type that identifies a type meeting the
* Allocator requirements.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t) and with return type @c type or a (possibly const) reference to @c
* type.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t,a) and with return type @c type or a (possibly const) reference to @c
* type.
*/
template <typename T, typename Allocator = std::allocator<void>>
struct associated_allocator
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_allocator_impl<T, Allocator>
#endif // !defined(GENERATING_DOCUMENTATION)
{
#if defined(GENERATING_DOCUMENTATION)
/// If @c T has a nested type @c allocator_type, <tt>T::allocator_type</tt>.
/// Otherwise @c Allocator.
typedef see_below type;
/// If @c T has a nested type @c allocator_type, returns
/// <tt>t.get_allocator()</tt>. Otherwise returns @c type().
static decltype(auto) get(const T& t) noexcept;
/// If @c T has a nested type @c allocator_type, returns
/// <tt>t.get_allocator()</tt>. Otherwise returns @c a.
static decltype(auto) get(const T& t, const Allocator& a) noexcept;
#endif // defined(GENERATING_DOCUMENTATION)
};
/// Helper function to obtain an object's associated allocator.
/**
* @returns <tt>associated_allocator<T>::get(t)</tt>
*/
template <typename T>
ASIO_NODISCARD inline typename associated_allocator<T>::type
get_associated_allocator(const T& t) noexcept
{
return associated_allocator<T>::get(t);
}
/// Helper function to obtain an object's associated allocator.
/**
* @returns <tt>associated_allocator<T, Allocator>::get(t, a)</tt>
*/
template <typename T, typename Allocator>
ASIO_NODISCARD inline auto get_associated_allocator(
const T& t, const Allocator& a) noexcept
-> decltype(associated_allocator<T, Allocator>::get(t, a))
{
return associated_allocator<T, Allocator>::get(t, a);
}
template <typename T, typename Allocator = std::allocator<void>>
using associated_allocator_t
= typename associated_allocator<T, Allocator>::type;
namespace detail {
template <typename T, typename A, typename = void>
struct associated_allocator_forwarding_base
{
};
template <typename T, typename A>
struct associated_allocator_forwarding_base<T, A,
enable_if_t<
is_same<
typename associated_allocator<T,
A>::asio_associated_allocator_is_unspecialised,
void
>::value
>>
{
typedef void asio_associated_allocator_is_unspecialised;
};
} // namespace detail
/// Specialisation of associated_allocator for @c std::reference_wrapper.
template <typename T, typename Allocator>
struct associated_allocator<reference_wrapper<T>, Allocator>
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_allocator_forwarding_base<T, Allocator>
#endif // !defined(GENERATING_DOCUMENTATION)
{
/// Forwards @c type to the associator specialisation for the unwrapped type
/// @c T.
typedef typename associated_allocator<T, Allocator>::type type;
/// Forwards the request to get the allocator to the associator specialisation
/// for the unwrapped type @c T.
static type get(reference_wrapper<T> t) noexcept
{
return associated_allocator<T, Allocator>::get(t.get());
}
/// Forwards the request to get the allocator to the associator specialisation
/// for the unwrapped type @c T.
static auto get(reference_wrapper<T> t, const Allocator& a) noexcept
-> decltype(associated_allocator<T, Allocator>::get(t.get(), a))
{
return associated_allocator<T, Allocator>::get(t.get(), a);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_ASSOCIATED_ALLOCATOR_HPP

View File

@@ -1,221 +0,0 @@
//
// associated_cancellation_slot.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ASSOCIATED_CANCELLATION_SLOT_HPP
#define ASIO_ASSOCIATED_CANCELLATION_SLOT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/associator.hpp"
#include "asio/cancellation_signal.hpp"
#include "asio/detail/functional.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
template <typename T, typename CancellationSlot>
struct associated_cancellation_slot;
namespace detail {
template <typename T, typename = void>
struct has_cancellation_slot_type : false_type
{
};
template <typename T>
struct has_cancellation_slot_type<T, void_t<typename T::cancellation_slot_type>>
: true_type
{
};
template <typename T, typename S, typename = void, typename = void>
struct associated_cancellation_slot_impl
{
typedef void asio_associated_cancellation_slot_is_unspecialised;
typedef S type;
static type get(const T&) noexcept
{
return type();
}
static const type& get(const T&, const S& s) noexcept
{
return s;
}
};
template <typename T, typename S>
struct associated_cancellation_slot_impl<T, S,
void_t<typename T::cancellation_slot_type>>
{
typedef typename T::cancellation_slot_type type;
static auto get(const T& t) noexcept
-> decltype(t.get_cancellation_slot())
{
return t.get_cancellation_slot();
}
static auto get(const T& t, const S&) noexcept
-> decltype(t.get_cancellation_slot())
{
return t.get_cancellation_slot();
}
};
template <typename T, typename S>
struct associated_cancellation_slot_impl<T, S,
enable_if_t<
!has_cancellation_slot_type<T>::value
>,
void_t<
typename associator<associated_cancellation_slot, T, S>::type
>> : associator<associated_cancellation_slot, T, S>
{
};
} // namespace detail
/// Traits type used to obtain the cancellation_slot associated with an object.
/**
* A program may specialise this traits type if the @c T template parameter in
* the specialisation is a user-defined type. The template parameter @c
* CancellationSlot shall be a type meeting the CancellationSlot requirements.
*
* Specialisations shall meet the following requirements, where @c t is a const
* reference to an object of type @c T, and @c s is an object of type @c
* CancellationSlot.
*
* @li Provide a nested typedef @c type that identifies a type meeting the
* CancellationSlot requirements.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t) and with return type @c type or a (possibly const) reference to @c
* type.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t,s) and with return type @c type or a (possibly const) reference to @c
* type.
*/
template <typename T, typename CancellationSlot = cancellation_slot>
struct associated_cancellation_slot
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_cancellation_slot_impl<T, CancellationSlot>
#endif // !defined(GENERATING_DOCUMENTATION)
{
#if defined(GENERATING_DOCUMENTATION)
/// If @c T has a nested type @c cancellation_slot_type,
/// <tt>T::cancellation_slot_type</tt>. Otherwise
/// @c CancellationSlot.
typedef see_below type;
/// If @c T has a nested type @c cancellation_slot_type, returns
/// <tt>t.get_cancellation_slot()</tt>. Otherwise returns @c type().
static decltype(auto) get(const T& t) noexcept;
/// If @c T has a nested type @c cancellation_slot_type, returns
/// <tt>t.get_cancellation_slot()</tt>. Otherwise returns @c s.
static decltype(auto) get(const T& t,
const CancellationSlot& s) noexcept;
#endif // defined(GENERATING_DOCUMENTATION)
};
/// Helper function to obtain an object's associated cancellation_slot.
/**
* @returns <tt>associated_cancellation_slot<T>::get(t)</tt>
*/
template <typename T>
ASIO_NODISCARD inline typename associated_cancellation_slot<T>::type
get_associated_cancellation_slot(const T& t) noexcept
{
return associated_cancellation_slot<T>::get(t);
}
/// Helper function to obtain an object's associated cancellation_slot.
/**
* @returns <tt>associated_cancellation_slot<T,
* CancellationSlot>::get(t, st)</tt>
*/
template <typename T, typename CancellationSlot>
ASIO_NODISCARD inline auto get_associated_cancellation_slot(
const T& t, const CancellationSlot& st) noexcept
-> decltype(associated_cancellation_slot<T, CancellationSlot>::get(t, st))
{
return associated_cancellation_slot<T, CancellationSlot>::get(t, st);
}
template <typename T, typename CancellationSlot = cancellation_slot>
using associated_cancellation_slot_t =
typename associated_cancellation_slot<T, CancellationSlot>::type;
namespace detail {
template <typename T, typename S, typename = void>
struct associated_cancellation_slot_forwarding_base
{
};
template <typename T, typename S>
struct associated_cancellation_slot_forwarding_base<T, S,
enable_if_t<
is_same<
typename associated_cancellation_slot<T,
S>::asio_associated_cancellation_slot_is_unspecialised,
void
>::value
>>
{
typedef void asio_associated_cancellation_slot_is_unspecialised;
};
} // namespace detail
/// Specialisation of associated_cancellation_slot for @c
/// std::reference_wrapper.
template <typename T, typename CancellationSlot>
struct associated_cancellation_slot<reference_wrapper<T>, CancellationSlot>
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_cancellation_slot_forwarding_base<T, CancellationSlot>
#endif // !defined(GENERATING_DOCUMENTATION)
{
/// Forwards @c type to the associator specialisation for the unwrapped type
/// @c T.
typedef typename associated_cancellation_slot<T, CancellationSlot>::type type;
/// Forwards the request to get the cancellation slot to the associator
/// specialisation for the unwrapped type @c T.
static type get(reference_wrapper<T> t) noexcept
{
return associated_cancellation_slot<T, CancellationSlot>::get(t.get());
}
/// Forwards the request to get the cancellation slot to the associator
/// specialisation for the unwrapped type @c T.
static auto get(reference_wrapper<T> t, const CancellationSlot& s) noexcept
-> decltype(
associated_cancellation_slot<T, CancellationSlot>::get(t.get(), s))
{
return associated_cancellation_slot<T, CancellationSlot>::get(t.get(), s);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_ASSOCIATED_CANCELLATION_SLOT_HPP

View File

@@ -1,235 +0,0 @@
//
// associated_executor.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ASSOCIATED_EXECUTOR_HPP
#define ASIO_ASSOCIATED_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/associator.hpp"
#include "asio/detail/functional.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/execution/executor.hpp"
#include "asio/is_executor.hpp"
#include "asio/system_executor.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
template <typename T, typename Executor>
struct associated_executor;
namespace detail {
template <typename T, typename = void>
struct has_executor_type : false_type
{
};
template <typename T>
struct has_executor_type<T, void_t<typename T::executor_type>>
: true_type
{
};
template <typename T, typename E, typename = void, typename = void>
struct associated_executor_impl
{
typedef void asio_associated_executor_is_unspecialised;
typedef E type;
static type get(const T&) noexcept
{
return type();
}
static const type& get(const T&, const E& e) noexcept
{
return e;
}
};
template <typename T, typename E>
struct associated_executor_impl<T, E, void_t<typename T::executor_type>>
{
typedef typename T::executor_type type;
static auto get(const T& t) noexcept
-> decltype(t.get_executor())
{
return t.get_executor();
}
static auto get(const T& t, const E&) noexcept
-> decltype(t.get_executor())
{
return t.get_executor();
}
};
template <typename T, typename E>
struct associated_executor_impl<T, E,
enable_if_t<
!has_executor_type<T>::value
>,
void_t<
typename associator<associated_executor, T, E>::type
>> : associator<associated_executor, T, E>
{
};
} // namespace detail
/// Traits type used to obtain the executor associated with an object.
/**
* A program may specialise this traits type if the @c T template parameter in
* the specialisation is a user-defined type. The template parameter @c
* Executor shall be a type meeting the Executor requirements.
*
* Specialisations shall meet the following requirements, where @c t is a const
* reference to an object of type @c T, and @c e is an object of type @c
* Executor.
*
* @li Provide a nested typedef @c type that identifies a type meeting the
* Executor requirements.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t) and with return type @c type or a (possibly const) reference to @c
* type.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t,e) and with return type @c type or a (possibly const) reference to @c
* type.
*/
template <typename T, typename Executor = system_executor>
struct associated_executor
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_executor_impl<T, Executor>
#endif // !defined(GENERATING_DOCUMENTATION)
{
#if defined(GENERATING_DOCUMENTATION)
/// If @c T has a nested type @c executor_type, <tt>T::executor_type</tt>.
/// Otherwise @c Executor.
typedef see_below type;
/// If @c T has a nested type @c executor_type, returns
/// <tt>t.get_executor()</tt>. Otherwise returns @c type().
static decltype(auto) get(const T& t) noexcept;
/// If @c T has a nested type @c executor_type, returns
/// <tt>t.get_executor()</tt>. Otherwise returns @c ex.
static decltype(auto) get(const T& t, const Executor& ex) noexcept;
#endif // defined(GENERATING_DOCUMENTATION)
};
/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_executor<T>::get(t)</tt>
*/
template <typename T>
ASIO_NODISCARD inline typename associated_executor<T>::type
get_associated_executor(const T& t) noexcept
{
return associated_executor<T>::get(t);
}
/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_executor<T, Executor>::get(t, ex)</tt>
*/
template <typename T, typename Executor>
ASIO_NODISCARD inline auto get_associated_executor(
const T& t, const Executor& ex,
constraint_t<
is_executor<Executor>::value || execution::is_executor<Executor>::value
> = 0) noexcept
-> decltype(associated_executor<T, Executor>::get(t, ex))
{
return associated_executor<T, Executor>::get(t, ex);
}
/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_executor<T, typename
* ExecutionContext::executor_type>::get(t, ctx.get_executor())</tt>
*/
template <typename T, typename ExecutionContext>
ASIO_NODISCARD inline typename associated_executor<T,
typename ExecutionContext::executor_type>::type
get_associated_executor(const T& t, ExecutionContext& ctx,
constraint_t<is_convertible<ExecutionContext&,
execution_context&>::value> = 0) noexcept
{
return associated_executor<T,
typename ExecutionContext::executor_type>::get(t, ctx.get_executor());
}
template <typename T, typename Executor = system_executor>
using associated_executor_t = typename associated_executor<T, Executor>::type;
namespace detail {
template <typename T, typename E, typename = void>
struct associated_executor_forwarding_base
{
};
template <typename T, typename E>
struct associated_executor_forwarding_base<T, E,
enable_if_t<
is_same<
typename associated_executor<T,
E>::asio_associated_executor_is_unspecialised,
void
>::value
>>
{
typedef void asio_associated_executor_is_unspecialised;
};
} // namespace detail
/// Specialisation of associated_executor for @c std::reference_wrapper.
template <typename T, typename Executor>
struct associated_executor<reference_wrapper<T>, Executor>
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_executor_forwarding_base<T, Executor>
#endif // !defined(GENERATING_DOCUMENTATION)
{
/// Forwards @c type to the associator specialisation for the unwrapped type
/// @c T.
typedef typename associated_executor<T, Executor>::type type;
/// Forwards the request to get the executor to the associator specialisation
/// for the unwrapped type @c T.
static type get(reference_wrapper<T> t) noexcept
{
return associated_executor<T, Executor>::get(t.get());
}
/// Forwards the request to get the executor to the associator specialisation
/// for the unwrapped type @c T.
static auto get(reference_wrapper<T> t, const Executor& ex) noexcept
-> decltype(associated_executor<T, Executor>::get(t.get(), ex))
{
return associated_executor<T, Executor>::get(t.get(), ex);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_ASSOCIATED_EXECUTOR_HPP

View File

@@ -1,281 +0,0 @@
//
// associated_immediate_executor.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ASSOCIATED_IMMEDIATE_EXECUTOR_HPP
#define ASIO_ASSOCIATED_IMMEDIATE_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/associator.hpp"
#include "asio/detail/functional.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/execution/blocking.hpp"
#include "asio/execution/executor.hpp"
#include "asio/execution_context.hpp"
#include "asio/is_executor.hpp"
#include "asio/require.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
template <typename T, typename Executor>
struct associated_immediate_executor;
namespace detail {
template <typename T, typename = void>
struct has_immediate_executor_type : false_type
{
};
template <typename T>
struct has_immediate_executor_type<T,
void_t<typename T::immediate_executor_type>>
: true_type
{
};
template <typename E, typename = void, typename = void>
struct default_immediate_executor
{
typedef decay_t<require_result_t<E, execution::blocking_t::never_t>> type;
static auto get(const E& e) noexcept
-> decltype(asio::require(e, execution::blocking.never))
{
return asio::require(e, execution::blocking.never);
}
};
template <typename E>
struct default_immediate_executor<E,
enable_if_t<
!execution::is_executor<E>::value
>,
enable_if_t<
is_executor<E>::value
>>
{
class type : public E
{
public:
template <typename Executor1>
explicit type(const Executor1& e,
constraint_t<
conditional_t<
!is_same<Executor1, type>::value,
is_convertible<Executor1, E>,
false_type
>::value
> = 0) noexcept
: E(e)
{
}
type(const type& other) noexcept
: E(static_cast<const E&>(other))
{
}
type(type&& other) noexcept
: E(static_cast<E&&>(other))
{
}
template <typename Function, typename Allocator>
void dispatch(Function&& f, const Allocator& a) const
{
this->post(static_cast<Function&&>(f), a);
}
friend bool operator==(const type& a, const type& b) noexcept
{
return static_cast<const E&>(a) == static_cast<const E&>(b);
}
friend bool operator!=(const type& a, const type& b) noexcept
{
return static_cast<const E&>(a) != static_cast<const E&>(b);
}
};
static type get(const E& e) noexcept
{
return type(e);
}
};
template <typename T, typename E, typename = void, typename = void>
struct associated_immediate_executor_impl
{
typedef void asio_associated_immediate_executor_is_unspecialised;
typedef typename default_immediate_executor<E>::type type;
static auto get(const T&, const E& e) noexcept
-> decltype(default_immediate_executor<E>::get(e))
{
return default_immediate_executor<E>::get(e);
}
};
template <typename T, typename E>
struct associated_immediate_executor_impl<T, E,
void_t<typename T::immediate_executor_type>>
{
typedef typename T::immediate_executor_type type;
static auto get(const T& t, const E&) noexcept
-> decltype(t.get_immediate_executor())
{
return t.get_immediate_executor();
}
};
template <typename T, typename E>
struct associated_immediate_executor_impl<T, E,
enable_if_t<
!has_immediate_executor_type<T>::value
>,
void_t<
typename associator<associated_immediate_executor, T, E>::type
>> : associator<associated_immediate_executor, T, E>
{
};
} // namespace detail
/// Traits type used to obtain the immediate executor associated with an object.
/**
* A program may specialise this traits type if the @c T template parameter in
* the specialisation is a user-defined type. The template parameter @c
* Executor shall be a type meeting the Executor requirements.
*
* Specialisations shall meet the following requirements, where @c t is a const
* reference to an object of type @c T, and @c e is an object of type @c
* Executor.
*
* @li Provide a nested typedef @c type that identifies a type meeting the
* Executor requirements.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t) and with return type @c type or a (possibly const) reference to @c
* type.
*
* @li Provide a noexcept static member function named @c get, callable as @c
* get(t,e) and with return type @c type or a (possibly const) reference to @c
* type.
*/
template <typename T, typename Executor>
struct associated_immediate_executor
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_immediate_executor_impl<T, Executor>
#endif // !defined(GENERATING_DOCUMENTATION)
{
#if defined(GENERATING_DOCUMENTATION)
/// If @c T has a nested type @c immediate_executor_type,
// <tt>T::immediate_executor_type</tt>. Otherwise @c Executor.
typedef see_below type;
/// If @c T has a nested type @c immediate_executor_type, returns
/// <tt>t.get_immediate_executor()</tt>. Otherwise returns
/// <tt>asio::require(ex, asio::execution::blocking.never)</tt>.
static decltype(auto) get(const T& t, const Executor& ex) noexcept;
#endif // defined(GENERATING_DOCUMENTATION)
};
/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_immediate_executor<T, Executor>::get(t, ex)</tt>
*/
template <typename T, typename Executor>
ASIO_NODISCARD inline auto get_associated_immediate_executor(
const T& t, const Executor& ex,
constraint_t<
is_executor<Executor>::value || execution::is_executor<Executor>::value
> = 0) noexcept
-> decltype(associated_immediate_executor<T, Executor>::get(t, ex))
{
return associated_immediate_executor<T, Executor>::get(t, ex);
}
/// Helper function to obtain an object's associated executor.
/**
* @returns <tt>associated_immediate_executor<T, typename
* ExecutionContext::executor_type>::get(t, ctx.get_executor())</tt>
*/
template <typename T, typename ExecutionContext>
ASIO_NODISCARD inline typename associated_immediate_executor<T,
typename ExecutionContext::executor_type>::type
get_associated_immediate_executor(const T& t, ExecutionContext& ctx,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value
> = 0) noexcept
{
return associated_immediate_executor<T,
typename ExecutionContext::executor_type>::get(t, ctx.get_executor());
}
template <typename T, typename Executor>
using associated_immediate_executor_t =
typename associated_immediate_executor<T, Executor>::type;
namespace detail {
template <typename T, typename E, typename = void>
struct associated_immediate_executor_forwarding_base
{
};
template <typename T, typename E>
struct associated_immediate_executor_forwarding_base<T, E,
enable_if_t<
is_same<
typename associated_immediate_executor<T,
E>::asio_associated_immediate_executor_is_unspecialised,
void
>::value
>>
{
typedef void asio_associated_immediate_executor_is_unspecialised;
};
} // namespace detail
/// Specialisation of associated_immediate_executor for
/// @c std::reference_wrapper.
template <typename T, typename Executor>
struct associated_immediate_executor<reference_wrapper<T>, Executor>
#if !defined(GENERATING_DOCUMENTATION)
: detail::associated_immediate_executor_forwarding_base<T, Executor>
#endif // !defined(GENERATING_DOCUMENTATION)
{
/// Forwards @c type to the associator specialisation for the unwrapped type
/// @c T.
typedef typename associated_immediate_executor<T, Executor>::type type;
/// Forwards the request to get the executor to the associator specialisation
/// for the unwrapped type @c T.
static auto get(reference_wrapper<T> t, const Executor& ex) noexcept
-> decltype(associated_immediate_executor<T, Executor>::get(t.get(), ex))
{
return associated_immediate_executor<T, Executor>::get(t.get(), ex);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_ASSOCIATED_IMMEDIATE_EXECUTOR_HPP

View File

@@ -1,35 +0,0 @@
//
// associator.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ASSOCIATOR_HPP
#define ASIO_ASSOCIATOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
/// Used to generically specialise associators for a type.
template <template <typename, typename> class Associator,
typename T, typename DefaultCandidate, typename _ = void>
struct associator
{
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_ASSOCIATOR_HPP

View File

@@ -1,948 +0,0 @@
//
// async_result.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_ASYNC_RESULT_HPP
#define ASIO_ASYNC_RESULT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
template <typename T>
struct is_completion_signature : false_type
{
};
template <typename R, typename... Args>
struct is_completion_signature<R(Args...)> : true_type
{
};
template <typename R, typename... Args>
struct is_completion_signature<R(Args...) &> : true_type
{
};
template <typename R, typename... Args>
struct is_completion_signature<R(Args...) &&> : true_type
{
};
# if defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename R, typename... Args>
struct is_completion_signature<R(Args...) noexcept> : true_type
{
};
template <typename R, typename... Args>
struct is_completion_signature<R(Args...) & noexcept> : true_type
{
};
template <typename R, typename... Args>
struct is_completion_signature<R(Args...) && noexcept> : true_type
{
};
# endif // defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename... T>
struct are_completion_signatures : false_type
{
};
template <>
struct are_completion_signatures<>
: true_type
{
};
template <typename T0>
struct are_completion_signatures<T0>
: is_completion_signature<T0>
{
};
template <typename T0, typename... TN>
struct are_completion_signatures<T0, TN...>
: integral_constant<bool, (
is_completion_signature<T0>::value
&& are_completion_signatures<TN...>::value)>
{
};
} // namespace detail
#if defined(ASIO_HAS_CONCEPTS)
namespace detail {
template <typename T, typename... Args>
ASIO_CONCEPT callable_with = requires(T&& t, Args&&... args)
{
static_cast<T&&>(t)(static_cast<Args&&>(args)...);
};
template <typename T, typename... Signatures>
struct is_completion_handler_for : false_type
{
};
template <typename T, typename R, typename... Args>
struct is_completion_handler_for<T, R(Args...)>
: integral_constant<bool, (callable_with<decay_t<T>, Args...>)>
{
};
template <typename T, typename R, typename... Args>
struct is_completion_handler_for<T, R(Args...) &>
: integral_constant<bool, (callable_with<decay_t<T>&, Args...>)>
{
};
template <typename T, typename R, typename... Args>
struct is_completion_handler_for<T, R(Args...) &&>
: integral_constant<bool, (callable_with<decay_t<T>&&, Args...>)>
{
};
# if defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename T, typename R, typename... Args>
struct is_completion_handler_for<T, R(Args...) noexcept>
: integral_constant<bool, (callable_with<decay_t<T>, Args...>)>
{
};
template <typename T, typename R, typename... Args>
struct is_completion_handler_for<T, R(Args...) & noexcept>
: integral_constant<bool, (callable_with<decay_t<T>&, Args...>)>
{
};
template <typename T, typename R, typename... Args>
struct is_completion_handler_for<T, R(Args...) && noexcept>
: integral_constant<bool, (callable_with<decay_t<T>&&, Args...>)>
{
};
# endif // defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename T, typename Signature0, typename... SignatureN>
struct is_completion_handler_for<T, Signature0, SignatureN...>
: integral_constant<bool, (
is_completion_handler_for<T, Signature0>::value
&& is_completion_handler_for<T, SignatureN...>::value)>
{
};
} // namespace detail
template <typename T>
ASIO_CONCEPT completion_signature =
detail::is_completion_signature<T>::value;
#define ASIO_COMPLETION_SIGNATURE \
::asio::completion_signature
template <typename T, typename... Signatures>
ASIO_CONCEPT completion_handler_for =
detail::are_completion_signatures<Signatures...>::value
&& detail::is_completion_handler_for<T, Signatures...>::value;
#define ASIO_COMPLETION_HANDLER_FOR(sig) \
::asio::completion_handler_for<sig>
#define ASIO_COMPLETION_HANDLER_FOR2(sig0, sig1) \
::asio::completion_handler_for<sig0, sig1>
#define ASIO_COMPLETION_HANDLER_FOR3(sig0, sig1, sig2) \
::asio::completion_handler_for<sig0, sig1, sig2>
#else // defined(ASIO_HAS_CONCEPTS)
#define ASIO_COMPLETION_SIGNATURE typename
#define ASIO_COMPLETION_HANDLER_FOR(sig) typename
#define ASIO_COMPLETION_HANDLER_FOR2(sig0, sig1) typename
#define ASIO_COMPLETION_HANDLER_FOR3(sig0, sig1, sig2) typename
#endif // defined(ASIO_HAS_CONCEPTS)
namespace detail {
template <typename T>
struct is_lvalue_completion_signature : false_type
{
};
template <typename R, typename... Args>
struct is_lvalue_completion_signature<R(Args...) &> : true_type
{
};
# if defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename R, typename... Args>
struct is_lvalue_completion_signature<R(Args...) & noexcept> : true_type
{
};
# endif // defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename... Signatures>
struct are_any_lvalue_completion_signatures : false_type
{
};
template <typename Sig0>
struct are_any_lvalue_completion_signatures<Sig0>
: is_lvalue_completion_signature<Sig0>
{
};
template <typename Sig0, typename... SigN>
struct are_any_lvalue_completion_signatures<Sig0, SigN...>
: integral_constant<bool, (
is_lvalue_completion_signature<Sig0>::value
|| are_any_lvalue_completion_signatures<SigN...>::value)>
{
};
template <typename T>
struct is_rvalue_completion_signature : false_type
{
};
template <typename R, typename... Args>
struct is_rvalue_completion_signature<R(Args...) &&> : true_type
{
};
# if defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename R, typename... Args>
struct is_rvalue_completion_signature<R(Args...) && noexcept> : true_type
{
};
# endif // defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename... Signatures>
struct are_any_rvalue_completion_signatures : false_type
{
};
template <typename Sig0>
struct are_any_rvalue_completion_signatures<Sig0>
: is_rvalue_completion_signature<Sig0>
{
};
template <typename Sig0, typename... SigN>
struct are_any_rvalue_completion_signatures<Sig0, SigN...>
: integral_constant<bool, (
is_rvalue_completion_signature<Sig0>::value
|| are_any_rvalue_completion_signatures<SigN...>::value)>
{
};
template <typename T>
struct simple_completion_signature;
template <typename R, typename... Args>
struct simple_completion_signature<R(Args...)>
{
typedef R type(Args...);
};
template <typename R, typename... Args>
struct simple_completion_signature<R(Args...) &>
{
typedef R type(Args...);
};
template <typename R, typename... Args>
struct simple_completion_signature<R(Args...) &&>
{
typedef R type(Args...);
};
# if defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename R, typename... Args>
struct simple_completion_signature<R(Args...) noexcept>
{
typedef R type(Args...);
};
template <typename R, typename... Args>
struct simple_completion_signature<R(Args...) & noexcept>
{
typedef R type(Args...);
};
template <typename R, typename... Args>
struct simple_completion_signature<R(Args...) && noexcept>
{
typedef R type(Args...);
};
# endif // defined(ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename CompletionToken,
ASIO_COMPLETION_SIGNATURE... Signatures>
class completion_handler_async_result
{
public:
typedef CompletionToken completion_handler_type;
typedef void return_type;
explicit completion_handler_async_result(completion_handler_type&)
{
}
return_type get()
{
}
template <typename Initiation,
ASIO_COMPLETION_HANDLER_FOR(Signatures...) RawCompletionToken,
typename... Args>
static return_type initiate(Initiation&& initiation,
RawCompletionToken&& token, Args&&... args)
{
static_cast<Initiation&&>(initiation)(
static_cast<RawCompletionToken&&>(token),
static_cast<Args&&>(args)...);
}
private:
completion_handler_async_result(
const completion_handler_async_result&) = delete;
completion_handler_async_result& operator=(
const completion_handler_async_result&) = delete;
};
} // namespace detail
#if defined(GENERATING_DOCUMENTATION)
/// An interface for customising the behaviour of an initiating function.
/**
* The async_result traits class is used for determining:
*
* @li the concrete completion handler type to be called at the end of the
* asynchronous operation;
*
* @li the initiating function return type; and
*
* @li how the return value of the initiating function is obtained.
*
* The trait allows the handler and return types to be determined at the point
* where the specific completion handler signature is known.
*
* This template may be specialised for user-defined completion token types.
* The primary template assumes that the CompletionToken is the completion
* handler.
*/
template <typename CompletionToken,
ASIO_COMPLETION_SIGNATURE... Signatures>
class async_result
{
public:
/// The concrete completion handler type for the specific signature.
typedef CompletionToken completion_handler_type;
/// The return type of the initiating function.
typedef void return_type;
/// Construct an async result from a given handler.
/**
* When using a specalised async_result, the constructor has an opportunity
* to initialise some state associated with the completion handler, which is
* then returned from the initiating function.
*/
explicit async_result(completion_handler_type& h);
/// Obtain the value to be returned from the initiating function.
return_type get();
/// Initiate the asynchronous operation that will produce the result, and
/// obtain the value to be returned from the initiating function.
template <typename Initiation, typename RawCompletionToken, typename... Args>
static return_type initiate(
Initiation&& initiation,
RawCompletionToken&& token,
Args&&... args);
private:
async_result(const async_result&) = delete;
async_result& operator=(const async_result&) = delete;
};
#else // defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken,
ASIO_COMPLETION_SIGNATURE... Signatures>
class async_result :
public conditional_t<
detail::are_any_lvalue_completion_signatures<Signatures...>::value
|| !detail::are_any_rvalue_completion_signatures<Signatures...>::value,
detail::completion_handler_async_result<CompletionToken, Signatures...>,
async_result<CompletionToken,
typename detail::simple_completion_signature<Signatures>::type...>
>
{
public:
typedef conditional_t<
detail::are_any_lvalue_completion_signatures<Signatures...>::value
|| !detail::are_any_rvalue_completion_signatures<Signatures...>::value,
detail::completion_handler_async_result<CompletionToken, Signatures...>,
async_result<CompletionToken,
typename detail::simple_completion_signature<Signatures>::type...>
> base_type;
using base_type::base_type;
private:
async_result(const async_result&) = delete;
async_result& operator=(const async_result&) = delete;
};
template <ASIO_COMPLETION_SIGNATURE... Signatures>
class async_result<void, Signatures...>
{
// Empty.
};
#endif // defined(GENERATING_DOCUMENTATION)
/// Helper template to deduce the handler type from a CompletionToken, capture
/// a local copy of the handler, and then create an async_result for the
/// handler.
template <typename CompletionToken,
ASIO_COMPLETION_SIGNATURE... Signatures>
struct async_completion
{
/// The real handler type to be used for the asynchronous operation.
typedef typename asio::async_result<
decay_t<CompletionToken>, Signatures...>::completion_handler_type
completion_handler_type;
/// Constructor.
/**
* The constructor creates the concrete completion handler and makes the link
* between the handler and the asynchronous result.
*/
explicit async_completion(CompletionToken& token)
: completion_handler(static_cast<conditional_t<
is_same<CompletionToken, completion_handler_type>::value,
completion_handler_type&, CompletionToken&&>>(token)),
result(completion_handler)
{
}
/// A copy of, or reference to, a real handler object.
conditional_t<
is_same<CompletionToken, completion_handler_type>::value,
completion_handler_type&, completion_handler_type> completion_handler;
/// The result of the asynchronous operation's initiating function.
async_result<decay_t<CompletionToken>, Signatures...> result;
};
namespace detail {
struct async_result_memfns_base
{
void initiate();
};
template <typename T>
struct async_result_memfns_derived
: T, async_result_memfns_base
{
};
template <typename T, T>
struct async_result_memfns_check
{
};
template <typename>
char (&async_result_initiate_memfn_helper(...))[2];
template <typename T>
char async_result_initiate_memfn_helper(
async_result_memfns_check<
void (async_result_memfns_base::*)(),
&async_result_memfns_derived<T>::initiate>*);
template <typename CompletionToken,
ASIO_COMPLETION_SIGNATURE... Signatures>
struct async_result_has_initiate_memfn
: integral_constant<bool, sizeof(async_result_initiate_memfn_helper<
async_result<decay_t<CompletionToken>, Signatures...>
>(0)) != 1>
{
};
} // namespace detail
#if defined(GENERATING_DOCUMENTATION)
# define ASIO_INITFN_RESULT_TYPE(ct, sig) \
void_or_deduced
# define ASIO_INITFN_RESULT_TYPE2(ct, sig0, sig1) \
void_or_deduced
# define ASIO_INITFN_RESULT_TYPE3(ct, sig0, sig1, sig2) \
void_or_deduced
#else
# define ASIO_INITFN_RESULT_TYPE(ct, sig) \
typename ::asio::async_result< \
typename ::asio::decay<ct>::type, sig>::return_type
# define ASIO_INITFN_RESULT_TYPE2(ct, sig0, sig1) \
typename ::asio::async_result< \
typename ::asio::decay<ct>::type, sig0, sig1>::return_type
# define ASIO_INITFN_RESULT_TYPE3(ct, sig0, sig1, sig2) \
typename ::asio::async_result< \
typename ::asio::decay<ct>::type, sig0, sig1, sig2>::return_type
#define ASIO_HANDLER_TYPE(ct, sig) \
typename ::asio::async_result< \
typename ::asio::decay<ct>::type, sig>::completion_handler_type
#define ASIO_HANDLER_TYPE2(ct, sig0, sig1) \
typename ::asio::async_result< \
typename ::asio::decay<ct>::type, \
sig0, sig1>::completion_handler_type
#define ASIO_HANDLER_TYPE3(ct, sig0, sig1, sig2) \
typename ::asio::async_result< \
typename ::asio::decay<ct>::type, \
sig0, sig1, sig2>::completion_handler_type
#endif
#if defined(GENERATING_DOCUMENTATION)
# define ASIO_INITFN_AUTO_RESULT_TYPE(ct, sig) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE2(ct, sig0, sig1) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE3(ct, sig0, sig1, sig2) \
auto
#elif defined(ASIO_HAS_RETURN_TYPE_DEDUCTION)
# define ASIO_INITFN_AUTO_RESULT_TYPE(ct, sig) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE2(ct, sig0, sig1) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE3(ct, sig0, sig1, sig2) \
auto
#else
# define ASIO_INITFN_AUTO_RESULT_TYPE(ct, sig) \
ASIO_INITFN_RESULT_TYPE(ct, sig)
# define ASIO_INITFN_AUTO_RESULT_TYPE2(ct, sig0, sig1) \
ASIO_INITFN_RESULT_TYPE2(ct, sig0, sig1)
# define ASIO_INITFN_AUTO_RESULT_TYPE3(ct, sig0, sig1, sig2) \
ASIO_INITFN_RESULT_TYPE3(ct, sig0, sig1, sig2)
#endif
#if defined(GENERATING_DOCUMENTATION)
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(ct, sig) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX2(ct, sig0, sig1) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX3(ct, sig0, sig1, sig2) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(expr)
#elif defined(ASIO_HAS_RETURN_TYPE_DEDUCTION)
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(ct, sig) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX2(ct, sig0, sig1) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX3(ct, sig0, sig1, sig2) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(expr)
#else
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(ct, sig) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX2(ct, sig0, sig1) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX3(ct, sig0, sig1, sig2) \
auto
# define ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(expr) -> decltype expr
#endif
#if defined(GENERATING_DOCUMENTATION)
# define ASIO_INITFN_DEDUCED_RESULT_TYPE(ct, sig, expr) \
void_or_deduced
# define ASIO_INITFN_DEDUCED_RESULT_TYPE2(ct, sig0, sig1, expr) \
void_or_deduced
# define ASIO_INITFN_DEDUCED_RESULT_TYPE3(ct, sig0, sig1, sig2, expr) \
void_or_deduced
#else
# define ASIO_INITFN_DEDUCED_RESULT_TYPE(ct, sig, expr) \
decltype expr
# define ASIO_INITFN_DEDUCED_RESULT_TYPE2(ct, sig0, sig1, expr) \
decltype expr
# define ASIO_INITFN_DEDUCED_RESULT_TYPE3(ct, sig0, sig1, sig2, expr) \
decltype expr
#endif
#if defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken,
completion_signature... Signatures,
typename Initiation, typename... Args>
void_or_deduced async_initiate(
Initiation&& initiation,
type_identity_t<CompletionToken>& token,
Args&&... args);
#else // defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken,
ASIO_COMPLETION_SIGNATURE... Signatures,
typename Initiation, typename... Args>
inline auto async_initiate(Initiation&& initiation,
type_identity_t<CompletionToken>& token, Args&&... args)
-> decltype(enable_if_t<
enable_if_t<
detail::are_completion_signatures<Signatures...>::value,
detail::async_result_has_initiate_memfn<
CompletionToken, Signatures...>>::value,
async_result<decay_t<CompletionToken>, Signatures...>>::initiate(
static_cast<Initiation&&>(initiation),
static_cast<CompletionToken&&>(token),
static_cast<Args&&>(args)...))
{
return async_result<decay_t<CompletionToken>, Signatures...>::initiate(
static_cast<Initiation&&>(initiation),
static_cast<CompletionToken&&>(token),
static_cast<Args&&>(args)...);
}
template <
ASIO_COMPLETION_SIGNATURE... Signatures,
typename CompletionToken, typename Initiation, typename... Args>
inline auto async_initiate(Initiation&& initiation,
CompletionToken&& token, Args&&... args)
-> decltype(enable_if_t<
enable_if_t<
detail::are_completion_signatures<Signatures...>::value,
detail::async_result_has_initiate_memfn<
CompletionToken, Signatures...>>::value,
async_result<decay_t<CompletionToken>, Signatures...>>::initiate(
static_cast<Initiation&&>(initiation),
static_cast<CompletionToken&&>(token),
static_cast<Args&&>(args)...))
{
return async_result<decay_t<CompletionToken>, Signatures...>::initiate(
static_cast<Initiation&&>(initiation),
static_cast<CompletionToken&&>(token),
static_cast<Args&&>(args)...);
}
template <typename CompletionToken,
ASIO_COMPLETION_SIGNATURE... Signatures,
typename Initiation, typename... Args>
inline typename enable_if_t<
!enable_if_t<
detail::are_completion_signatures<Signatures...>::value,
detail::async_result_has_initiate_memfn<
CompletionToken, Signatures...>>::value,
async_result<decay_t<CompletionToken>, Signatures...>
>::return_type
async_initiate(Initiation&& initiation,
type_identity_t<CompletionToken>& token, Args&&... args)
{
async_completion<CompletionToken, Signatures...> completion(token);
static_cast<Initiation&&>(initiation)(
static_cast<
typename async_result<decay_t<CompletionToken>,
Signatures...>::completion_handler_type&&>(
completion.completion_handler),
static_cast<Args&&>(args)...);
return completion.result.get();
}
template <ASIO_COMPLETION_SIGNATURE... Signatures,
typename CompletionToken, typename Initiation, typename... Args>
inline typename enable_if_t<
!enable_if_t<
detail::are_completion_signatures<Signatures...>::value,
detail::async_result_has_initiate_memfn<
CompletionToken, Signatures...>>::value,
async_result<decay_t<CompletionToken>, Signatures...>
>::return_type
async_initiate(Initiation&& initiation, CompletionToken&& token, Args&&... args)
{
async_completion<CompletionToken, Signatures...> completion(token);
static_cast<Initiation&&>(initiation)(
static_cast<
typename async_result<decay_t<CompletionToken>,
Signatures...>::completion_handler_type&&>(
completion.completion_handler),
static_cast<Args&&>(args)...);
return completion.result.get();
}
#endif // defined(GENERATING_DOCUMENTATION)
#if defined(ASIO_HAS_CONCEPTS)
namespace detail {
template <typename... Signatures>
struct initiation_archetype
{
template <completion_handler_for<Signatures...> CompletionHandler>
void operator()(CompletionHandler&&) const
{
}
};
} // namespace detail
template <typename T, typename... Signatures>
ASIO_CONCEPT completion_token_for =
detail::are_completion_signatures<Signatures...>::value
&&
requires(T&& t)
{
async_initiate<T, Signatures...>(
detail::initiation_archetype<Signatures...>{}, t);
};
#define ASIO_COMPLETION_TOKEN_FOR(sig) \
::asio::completion_token_for<sig>
#define ASIO_COMPLETION_TOKEN_FOR2(sig0, sig1) \
::asio::completion_token_for<sig0, sig1>
#define ASIO_COMPLETION_TOKEN_FOR3(sig0, sig1, sig2) \
::asio::completion_token_for<sig0, sig1, sig2>
#else // defined(ASIO_HAS_CONCEPTS)
#define ASIO_COMPLETION_TOKEN_FOR(sig) typename
#define ASIO_COMPLETION_TOKEN_FOR2(sig0, sig1) typename
#define ASIO_COMPLETION_TOKEN_FOR3(sig0, sig1, sig2) typename
#endif // defined(ASIO_HAS_CONCEPTS)
namespace detail {
struct async_operation_probe {};
struct async_operation_probe_result {};
template <typename Call, typename = void>
struct is_async_operation_call : false_type
{
};
template <typename Call>
struct is_async_operation_call<Call,
void_t<
enable_if_t<
is_same<
result_of_t<Call>,
async_operation_probe_result
>::value
>
>
> : true_type
{
};
} // namespace detail
#if !defined(GENERATING_DOCUMENTATION)
template <typename... Signatures>
class async_result<detail::async_operation_probe, Signatures...>
{
public:
typedef detail::async_operation_probe_result return_type;
template <typename Initiation, typename... InitArgs>
static return_type initiate(Initiation&&,
detail::async_operation_probe, InitArgs&&...)
{
return return_type();
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
#if defined(GENERATING_DOCUMENTATION)
/// The is_async_operation trait detects whether a type @c T and arguments
/// @c Args... may be used to initiate an asynchronous operation.
/**
* Class template @c is_async_operation is a trait is derived from @c true_type
* if the expression <tt>T(Args..., token)</tt> initiates an asynchronous
* operation, where @c token is an unspecified completion token type. Otherwise,
* @c is_async_operation is derived from @c false_type.
*/
template <typename T, typename... Args>
struct is_async_operation : integral_constant<bool, automatically_determined>
{
};
#else // defined(GENERATING_DOCUMENTATION)
template <typename T, typename... Args>
struct is_async_operation :
detail::is_async_operation_call<
T(Args..., detail::async_operation_probe)>
{
};
#endif // defined(GENERATING_DOCUMENTATION)
#if defined(ASIO_HAS_CONCEPTS)
template <typename T, typename... Args>
ASIO_CONCEPT async_operation = is_async_operation<T, Args...>::value;
#define ASIO_ASYNC_OPERATION(t) \
::asio::async_operation<t>
#define ASIO_ASYNC_OPERATION1(t, a0) \
::asio::async_operation<t, a0>
#define ASIO_ASYNC_OPERATION2(t, a0, a1) \
::asio::async_operation<t, a0, a1>
#define ASIO_ASYNC_OPERATION3(t, a0, a1, a2) \
::asio::async_operation<t, a0, a1, a2>
#else // defined(ASIO_HAS_CONCEPTS)
#define ASIO_ASYNC_OPERATION(t) typename
#define ASIO_ASYNC_OPERATION1(t, a0) typename
#define ASIO_ASYNC_OPERATION2(t, a0, a1) typename
#define ASIO_ASYNC_OPERATION3(t, a0, a1, a2) typename
#endif // defined(ASIO_HAS_CONCEPTS)
namespace detail {
struct completion_signature_probe {};
template <typename... T>
struct completion_signature_probe_result
{
template <template <typename...> class Op>
struct apply
{
typedef Op<T...> type;
};
};
template <typename T>
struct completion_signature_probe_result<T>
{
typedef T type;
template <template <typename...> class Op>
struct apply
{
typedef Op<T> type;
};
};
template <>
struct completion_signature_probe_result<void>
{
template <template <typename...> class Op>
struct apply
{
typedef Op<> type;
};
};
} // namespace detail
#if !defined(GENERATING_DOCUMENTATION)
template <typename... Signatures>
class async_result<detail::completion_signature_probe, Signatures...>
{
public:
typedef detail::completion_signature_probe_result<Signatures...> return_type;
template <typename Initiation, typename... InitArgs>
static return_type initiate(Initiation&&,
detail::completion_signature_probe, InitArgs&&...)
{
return return_type();
}
};
template <typename Signature>
class async_result<detail::completion_signature_probe, Signature>
{
public:
typedef detail::completion_signature_probe_result<Signature> return_type;
template <typename Initiation, typename... InitArgs>
static return_type initiate(Initiation&&,
detail::completion_signature_probe, InitArgs&&...)
{
return return_type();
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
#if defined(GENERATING_DOCUMENTATION)
/// The completion_signature_of trait determines the completion signature
/// of an asynchronous operation.
/**
* Class template @c completion_signature_of is a trait with a member type
* alias @c type that denotes the completion signature of the asynchronous
* operation initiated by the expression <tt>T(Args..., token)</tt> operation,
* where @c token is an unspecified completion token type. If the asynchronous
* operation does not have exactly one completion signature, the instantion of
* the trait is well-formed but the member type alias @c type is omitted. If
* the expression <tt>T(Args..., token)</tt> is not an asynchronous operation
* then use of the trait is ill-formed.
*/
template <typename T, typename... Args>
struct completion_signature_of
{
typedef automatically_determined type;
};
#else // defined(GENERATING_DOCUMENTATION)
template <typename T, typename... Args>
struct completion_signature_of :
result_of_t<T(Args..., detail::completion_signature_probe)>
{
};
#endif // defined(GENERATING_DOCUMENTATION)
template <typename T, typename... Args>
using completion_signature_of_t =
typename completion_signature_of<T, Args...>::type;
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/default_completion_token.hpp"
#endif // ASIO_ASYNC_RESULT_HPP

View File

@@ -1,142 +0,0 @@
//
// awaitable.hpp
// ~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_AWAITABLE_HPP
#define ASIO_AWAITABLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
#if defined(ASIO_HAS_STD_COROUTINE)
# include <coroutine>
#else // defined(ASIO_HAS_STD_COROUTINE)
# include <experimental/coroutine>
#endif // defined(ASIO_HAS_STD_COROUTINE)
#include <utility>
#include "asio/any_io_executor.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
#if defined(ASIO_HAS_STD_COROUTINE)
using std::coroutine_handle;
using std::suspend_always;
#else // defined(ASIO_HAS_STD_COROUTINE)
using std::experimental::coroutine_handle;
using std::experimental::suspend_always;
#endif // defined(ASIO_HAS_STD_COROUTINE)
template <typename> class awaitable_thread;
template <typename, typename> class awaitable_frame;
} // namespace detail
/// The return type of a coroutine or asynchronous operation.
template <typename T, typename Executor = any_io_executor>
class ASIO_NODISCARD awaitable
{
public:
/// The type of the awaited value.
typedef T value_type;
/// The executor type that will be used for the coroutine.
typedef Executor executor_type;
/// Default constructor.
constexpr awaitable() noexcept
: frame_(nullptr)
{
}
/// Move constructor.
awaitable(awaitable&& other) noexcept
: frame_(std::exchange(other.frame_, nullptr))
{
}
/// Destructor
~awaitable()
{
if (frame_)
frame_->destroy();
}
/// Move assignment.
awaitable& operator=(awaitable&& other) noexcept
{
if (this != &other)
frame_ = std::exchange(other.frame_, nullptr);
return *this;
}
/// Checks if the awaitable refers to a future result.
bool valid() const noexcept
{
return !!frame_;
}
#if !defined(GENERATING_DOCUMENTATION)
// Support for co_await keyword.
bool await_ready() const noexcept
{
return false;
}
// Support for co_await keyword.
template <class U>
void await_suspend(
detail::coroutine_handle<detail::awaitable_frame<U, Executor>> h)
{
frame_->push_frame(&h.promise());
}
// Support for co_await keyword.
T await_resume()
{
return awaitable(static_cast<awaitable&&>(*this)).frame_->get();
}
#endif // !defined(GENERATING_DOCUMENTATION)
private:
template <typename> friend class detail::awaitable_thread;
template <typename, typename> friend class detail::awaitable_frame;
// Not copy constructible or copy assignable.
awaitable(const awaitable&) = delete;
awaitable& operator=(const awaitable&) = delete;
// Construct the awaitable from a coroutine's frame object.
explicit awaitable(detail::awaitable_frame<T, Executor>* a)
: frame_(a)
{
}
detail::awaitable_frame<T, Executor>* frame_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/impl/awaitable.hpp"
#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
#endif // ASIO_AWAITABLE_HPP

View File

@@ -1,710 +0,0 @@
//
// basic_deadline_timer.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP
#define ASIO_BASIC_DEADLINE_TIMER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_BOOST_DATE_TIME) \
|| defined(GENERATING_DOCUMENTATION)
#include <cstddef>
#include "asio/any_io_executor.hpp"
#include "asio/detail/deadline_timer_service.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/io_object_impl.hpp"
#include "asio/detail/non_const_lvalue.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/execution_context.hpp"
#include "asio/time_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
/// Provides waitable timer functionality.
/**
* The basic_deadline_timer class template provides the ability to perform a
* blocking or asynchronous wait for a timer to expire.
*
* A deadline timer is always in one of two states: "expired" or "not expired".
* If the wait() or async_wait() function is called on an expired timer, the
* wait operation will complete immediately.
*
* Most applications will use the asio::deadline_timer typedef.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* @par Examples
* Performing a blocking wait:
* @code
* // Construct a timer without setting an expiry time.
* asio::deadline_timer timer(my_context);
*
* // Set an expiry time relative to now.
* timer.expires_from_now(boost::posix_time::seconds(5));
*
* // Wait for the timer to expire.
* timer.wait();
* @endcode
*
* @par
* Performing an asynchronous wait:
* @code
* void handler(const asio::error_code& error)
* {
* if (!error)
* {
* // Timer expired.
* }
* }
*
* ...
*
* // Construct a timer with an absolute expiry time.
* asio::deadline_timer timer(my_context,
* boost::posix_time::time_from_string("2005-12-07 23:59:59.000"));
*
* // Start an asynchronous wait.
* timer.async_wait(handler);
* @endcode
*
* @par Changing an active deadline_timer's expiry time
*
* Changing the expiry time of a timer while there are pending asynchronous
* waits causes those wait operations to be cancelled. To ensure that the action
* associated with the timer is performed only once, use something like this:
* used:
*
* @code
* void on_some_event()
* {
* if (my_timer.expires_from_now(seconds(5)) > 0)
* {
* // We managed to cancel the timer. Start new asynchronous wait.
* my_timer.async_wait(on_timeout);
* }
* else
* {
* // Too late, timer has already expired!
* }
* }
*
* void on_timeout(const asio::error_code& e)
* {
* if (e != asio::error::operation_aborted)
* {
* // Timer was not cancelled, take necessary action.
* }
* }
* @endcode
*
* @li The asio::basic_deadline_timer::expires_from_now() function
* cancels any pending asynchronous waits, and returns the number of
* asynchronous waits that were cancelled. If it returns 0 then you were too
* late and the wait handler has already been executed, or will soon be
* executed. If it returns 1 then the wait handler was successfully cancelled.
*
* @li If a wait handler is cancelled, the asio::error_code passed to
* it contains the value asio::error::operation_aborted.
*/
template <typename Time,
typename TimeTraits = asio::time_traits<Time>,
typename Executor = any_io_executor>
class basic_deadline_timer
{
private:
class initiate_async_wait;
public:
/// The type of the executor associated with the object.
typedef Executor executor_type;
/// Rebinds the timer type to another executor.
template <typename Executor1>
struct rebind_executor
{
/// The timer type when rebound to the specified executor.
typedef basic_deadline_timer<Time, TimeTraits, Executor1> other;
};
/// The time traits type.
typedef TimeTraits traits_type;
/// The time type.
typedef typename traits_type::time_type time_type;
/// The duration type.
typedef typename traits_type::duration_type duration_type;
/// Constructor.
/**
* This constructor creates a timer without setting an expiry time. The
* expires_at() or expires_from_now() functions must be called to set an
* expiry time before the timer can be waited on.
*
* @param ex The I/O executor that the timer will use, by default, to
* dispatch handlers for any asynchronous operations performed on the timer.
*/
explicit basic_deadline_timer(const executor_type& ex)
: impl_(0, ex)
{
}
/// Constructor.
/**
* This constructor creates a timer without setting an expiry time. The
* expires_at() or expires_from_now() functions must be called to set an
* expiry time before the timer can be waited on.
*
* @param context An execution context which provides the I/O executor that
* the timer will use, by default, to dispatch handlers for any asynchronous
* operations performed on the timer.
*/
template <typename ExecutionContext>
explicit basic_deadline_timer(ExecutionContext& context,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value
> = 0)
: impl_(0, 0, context)
{
}
/// Constructor to set a particular expiry time as an absolute time.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param ex The I/O executor that the timer will use, by default, to
* dispatch handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, expressed
* as an absolute time.
*/
basic_deadline_timer(const executor_type& ex, const time_type& expiry_time)
: impl_(0, ex)
{
asio::error_code ec;
impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");
}
/// Constructor to set a particular expiry time as an absolute time.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param context An execution context which provides the I/O executor that
* the timer will use, by default, to dispatch handlers for any asynchronous
* operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, expressed
* as an absolute time.
*/
template <typename ExecutionContext>
basic_deadline_timer(ExecutionContext& context, const time_type& expiry_time,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value
> = 0)
: impl_(0, 0, context)
{
asio::error_code ec;
impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");
}
/// Constructor to set a particular expiry time relative to now.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param ex The I/O executor that the timer will use, by default, to
* dispatch handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, relative to
* now.
*/
basic_deadline_timer(const executor_type& ex,
const duration_type& expiry_time)
: impl_(0, ex)
{
asio::error_code ec;
impl_.get_service().expires_from_now(
impl_.get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");
}
/// Constructor to set a particular expiry time relative to now.
/**
* This constructor creates a timer and sets the expiry time.
*
* @param context An execution context which provides the I/O executor that
* the timer will use, by default, to dispatch handlers for any asynchronous
* operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, relative to
* now.
*/
template <typename ExecutionContext>
basic_deadline_timer(ExecutionContext& context,
const duration_type& expiry_time,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value
> = 0)
: impl_(0, 0, context)
{
asio::error_code ec;
impl_.get_service().expires_from_now(
impl_.get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");
}
/// Move-construct a basic_deadline_timer from another.
/**
* This constructor moves a timer from one object to another.
*
* @param other The other basic_deadline_timer object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_deadline_timer(const executor_type&)
* constructor.
*/
basic_deadline_timer(basic_deadline_timer&& other)
: impl_(std::move(other.impl_))
{
}
/// Move-assign a basic_deadline_timer from another.
/**
* This assignment operator moves a timer from one object to another. Cancels
* any outstanding asynchronous operations associated with the target object.
*
* @param other The other basic_deadline_timer object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_deadline_timer(const executor_type&)
* constructor.
*/
basic_deadline_timer& operator=(basic_deadline_timer&& other)
{
impl_ = std::move(other.impl_);
return *this;
}
/// Destroys the timer.
/**
* This function destroys the timer, cancelling any outstanding asynchronous
* wait operations associated with the timer as if by calling @c cancel.
*/
~basic_deadline_timer()
{
}
/// Get the executor associated with the object.
const executor_type& get_executor() noexcept
{
return impl_.get_executor();
}
/// Cancel any asynchronous operations that are waiting on the timer.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the timer. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel()
{
asio::error_code ec;
std::size_t s = impl_.get_service().cancel(impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "cancel");
return s;
}
/// Cancel any asynchronous operations that are waiting on the timer.
/**
* This function forces the completion of any pending asynchronous wait
* operations against the timer. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when cancel() is called, then the
* handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel(asio::error_code& ec)
{
return impl_.get_service().cancel(impl_.get_implementation(), ec);
}
/// Cancels one asynchronous operation that is waiting on the timer.
/**
* This function forces the completion of one pending asynchronous wait
* operation against the timer. Handlers are cancelled in FIFO order. The
* handler for the cancelled operation will be invoked with the
* asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @return The number of asynchronous operations that were cancelled. That is,
* either 0 or 1.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when cancel_one() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel_one()
{
asio::error_code ec;
std::size_t s = impl_.get_service().cancel_one(
impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "cancel_one");
return s;
}
/// Cancels one asynchronous operation that is waiting on the timer.
/**
* This function forces the completion of one pending asynchronous wait
* operation against the timer. Handlers are cancelled in FIFO order. The
* handler for the cancelled operation will be invoked with the
* asio::error::operation_aborted error code.
*
* Cancelling the timer does not change the expiry time.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled. That is,
* either 0 or 1.
*
* @note If the timer has already expired when cancel_one() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t cancel_one(asio::error_code& ec)
{
return impl_.get_service().cancel_one(impl_.get_implementation(), ec);
}
/// Get the timer's expiry time as an absolute time.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
time_type expires_at() const
{
return impl_.get_service().expires_at(impl_.get_implementation());
}
/// Set the timer's expiry time as an absolute time.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when expires_at() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_at(const time_type& expiry_time)
{
asio::error_code ec;
std::size_t s = impl_.get_service().expires_at(
impl_.get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_at");
return s;
}
/// Set the timer's expiry time as an absolute time.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when expires_at() is called, then
* the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_at(const time_type& expiry_time,
asio::error_code& ec)
{
return impl_.get_service().expires_at(
impl_.get_implementation(), expiry_time, ec);
}
/// Get the timer's expiry time relative to now.
/**
* This function may be used to obtain the timer's current expiry time.
* Whether the timer has expired or not does not affect this value.
*/
duration_type expires_from_now() const
{
return impl_.get_service().expires_from_now(impl_.get_implementation());
}
/// Set the timer's expiry time relative to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @return The number of asynchronous operations that were cancelled.
*
* @throws asio::system_error Thrown on failure.
*
* @note If the timer has already expired when expires_from_now() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_from_now(const duration_type& expiry_time)
{
asio::error_code ec;
std::size_t s = impl_.get_service().expires_from_now(
impl_.get_implementation(), expiry_time, ec);
asio::detail::throw_error(ec, "expires_from_now");
return s;
}
/// Set the timer's expiry time relative to now.
/**
* This function sets the expiry time. Any pending asynchronous wait
* operations will be cancelled. The handler for each cancelled operation will
* be invoked with the asio::error::operation_aborted error code.
*
* @param expiry_time The expiry time to be used for the timer.
*
* @param ec Set to indicate what error occurred, if any.
*
* @return The number of asynchronous operations that were cancelled.
*
* @note If the timer has already expired when expires_from_now() is called,
* then the handlers for asynchronous wait operations will:
*
* @li have already been invoked; or
*
* @li have been queued for invocation in the near future.
*
* These handlers can no longer be cancelled, and therefore are passed an
* error code that indicates the successful completion of the wait operation.
*/
std::size_t expires_from_now(const duration_type& expiry_time,
asio::error_code& ec)
{
return impl_.get_service().expires_from_now(
impl_.get_implementation(), expiry_time, ec);
}
/// Perform a blocking wait on the timer.
/**
* This function is used to wait for the timer to expire. This function
* blocks and does not return until the timer has expired.
*
* @throws asio::system_error Thrown on failure.
*/
void wait()
{
asio::error_code ec;
impl_.get_service().wait(impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "wait");
}
/// Perform a blocking wait on the timer.
/**
* This function is used to wait for the timer to expire. This function
* blocks and does not return until the timer has expired.
*
* @param ec Set to indicate what error occurred, if any.
*/
void wait(asio::error_code& ec)
{
impl_.get_service().wait(impl_.get_implementation(), ec);
}
/// Start an asynchronous wait on the timer.
/**
* This function may be used to initiate an asynchronous wait against the
* timer. It is an initiating function for an @ref asynchronous_operation,
* and always returns immediately.
*
* For each call to async_wait(), the completion handler will be called
* exactly once. The completion handler will be called when:
*
* @li The timer has expired.
*
* @li The timer was cancelled, in which case the handler is passed the error
* code asio::error::operation_aborted.
*
* @param token The @ref completion_token that will be used to produce a
* completion handler, which will be called when the timer expires. Potential
* completion tokens include @ref use_future, @ref use_awaitable, @ref
* yield_context, or a function object with the correct completion signature.
* The function signature of the completion handler must be:
* @code void handler(
* const asio::error_code& error // Result of operation.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the completion handler will not be invoked from within this function.
* On immediate completion, invocation of the handler will be performed in a
* manner equivalent to using asio::async_immediate().
*
* @par Completion Signature
* @code void(asio::error_code) @endcode
*
* @par Per-Operation Cancellation
* This asynchronous operation supports cancellation for the following
* asio::cancellation_type values:
*
* @li @c cancellation_type::terminal
*
* @li @c cancellation_type::partial
*
* @li @c cancellation_type::total
*/
template <
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
WaitToken = default_completion_token_t<executor_type>>
auto async_wait(
WaitToken&& token = default_completion_token_t<executor_type>())
-> decltype(
async_initiate<WaitToken, void (asio::error_code)>(
declval<initiate_async_wait>(), token))
{
return async_initiate<WaitToken, void (asio::error_code)>(
initiate_async_wait(this), token);
}
private:
// Disallow copying and assignment.
basic_deadline_timer(const basic_deadline_timer&) = delete;
basic_deadline_timer& operator=(
const basic_deadline_timer&) = delete;
class initiate_async_wait
{
public:
typedef Executor executor_type;
explicit initiate_async_wait(basic_deadline_timer* self)
: self_(self)
{
}
const executor_type& get_executor() const noexcept
{
return self_->get_executor();
}
template <typename WaitHandler>
void operator()(WaitHandler&& handler) const
{
// If you get an error on the following line it means that your handler
// does not meet the documented type requirements for a WaitHandler.
ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check;
detail::non_const_lvalue<WaitHandler> handler2(handler);
self_->impl_.get_service().async_wait(
self_->impl_.get_implementation(),
handler2.value, self_->impl_.get_executor());
}
private:
basic_deadline_timer* self_;
};
detail::io_object_impl<
detail::deadline_timer_service<TimeTraits>, Executor> impl_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
// || defined(GENERATING_DOCUMENTATION)
#endif // ASIO_BASIC_DEADLINE_TIMER_HPP

View File

@@ -1,824 +0,0 @@
//
// basic_file.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_FILE_HPP
#define ASIO_BASIC_FILE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_FILE) \
|| defined(GENERATING_DOCUMENTATION)
#include <string>
#include <utility>
#include "asio/any_io_executor.hpp"
#include "asio/async_result.hpp"
#include "asio/detail/cstdint.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/io_object_impl.hpp"
#include "asio/detail/non_const_lvalue.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/error.hpp"
#include "asio/execution_context.hpp"
#include "asio/post.hpp"
#include "asio/file_base.hpp"
#if defined(ASIO_HAS_IOCP)
# include "asio/detail/win_iocp_file_service.hpp"
#elif defined(ASIO_HAS_IO_URING)
# include "asio/detail/io_uring_file_service.hpp"
#endif
#include "asio/detail/push_options.hpp"
namespace asio {
#if !defined(ASIO_BASIC_FILE_FWD_DECL)
#define ASIO_BASIC_FILE_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Executor = any_io_executor>
class basic_file;
#endif // !defined(ASIO_BASIC_FILE_FWD_DECL)
/// Provides file functionality.
/**
* The basic_file class template provides functionality that is common to both
* stream-oriented and random-access files.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*/
template <typename Executor>
class basic_file
: public file_base
{
public:
/// The type of the executor associated with the object.
typedef Executor executor_type;
/// Rebinds the file type to another executor.
template <typename Executor1>
struct rebind_executor
{
/// The file type when rebound to the specified executor.
typedef basic_file<Executor1> other;
};
/// The native representation of a file.
#if defined(GENERATING_DOCUMENTATION)
typedef implementation_defined native_handle_type;
#elif defined(ASIO_HAS_IOCP)
typedef detail::win_iocp_file_service::native_handle_type native_handle_type;
#elif defined(ASIO_HAS_IO_URING)
typedef detail::io_uring_file_service::native_handle_type native_handle_type;
#endif
/// Construct a basic_file without opening it.
/**
* This constructor initialises a file without opening it.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*/
explicit basic_file(const executor_type& ex)
: impl_(0, ex)
{
}
/// Construct a basic_file without opening it.
/**
* This constructor initialises a file without opening it.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*/
template <typename ExecutionContext>
explicit basic_file(ExecutionContext& context,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: impl_(0, 0, context)
{
}
/// Construct and open a basic_file.
/**
* This constructor initialises a file and opens it.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*/
explicit basic_file(const executor_type& ex,
const char* path, file_base::flags open_flags)
: impl_(0, ex)
{
asio::error_code ec;
impl_.get_service().open(impl_.get_implementation(), path, open_flags, ec);
asio::detail::throw_error(ec, "open");
}
/// Construct a basic_file without opening it.
/**
* This constructor initialises a file and opens it.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*/
template <typename ExecutionContext>
explicit basic_file(ExecutionContext& context,
const char* path, file_base::flags open_flags,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: impl_(0, 0, context)
{
asio::error_code ec;
impl_.get_service().open(impl_.get_implementation(), path, open_flags, ec);
asio::detail::throw_error(ec, "open");
}
/// Construct and open a basic_file.
/**
* This constructor initialises a file and opens it.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*/
explicit basic_file(const executor_type& ex,
const std::string& path, file_base::flags open_flags)
: impl_(0, ex)
{
asio::error_code ec;
impl_.get_service().open(impl_.get_implementation(),
path.c_str(), open_flags, ec);
asio::detail::throw_error(ec, "open");
}
/// Construct a basic_file without opening it.
/**
* This constructor initialises a file and opens it.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*/
template <typename ExecutionContext>
explicit basic_file(ExecutionContext& context,
const std::string& path, file_base::flags open_flags,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: impl_(0, 0, context)
{
asio::error_code ec;
impl_.get_service().open(impl_.get_implementation(),
path.c_str(), open_flags, ec);
asio::detail::throw_error(ec, "open");
}
/// Construct a basic_file on an existing native file handle.
/**
* This constructor initialises a file object to hold an existing native file.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*
* @param native_file A native file handle.
*
* @throws asio::system_error Thrown on failure.
*/
basic_file(const executor_type& ex, const native_handle_type& native_file)
: impl_(0, ex)
{
asio::error_code ec;
impl_.get_service().assign(
impl_.get_implementation(), native_file, ec);
asio::detail::throw_error(ec, "assign");
}
/// Construct a basic_file on an existing native file.
/**
* This constructor initialises a file object to hold an existing native file.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*
* @param native_file A native file.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename ExecutionContext>
basic_file(ExecutionContext& context, const native_handle_type& native_file,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: impl_(0, 0, context)
{
asio::error_code ec;
impl_.get_service().assign(
impl_.get_implementation(), native_file, ec);
asio::detail::throw_error(ec, "assign");
}
/// Move-construct a basic_file from another.
/**
* This constructor moves a file from one object to another.
*
* @param other The other basic_file object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_file(const executor_type&) constructor.
*/
basic_file(basic_file&& other) noexcept
: impl_(std::move(other.impl_))
{
}
/// Move-assign a basic_file from another.
/**
* This assignment operator moves a file from one object to another.
*
* @param other The other basic_file object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_file(const executor_type&) constructor.
*/
basic_file& operator=(basic_file&& other)
{
impl_ = std::move(other.impl_);
return *this;
}
// All files have access to each other's implementations.
template <typename Executor1>
friend class basic_file;
/// Move-construct a basic_file from a file of another executor type.
/**
* This constructor moves a file from one object to another.
*
* @param other The other basic_file object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_file(const executor_type&) constructor.
*/
template <typename Executor1>
basic_file(basic_file<Executor1>&& other,
constraint_t<
is_convertible<Executor1, Executor>::value,
defaulted_constraint
> = defaulted_constraint())
: impl_(std::move(other.impl_))
{
}
/// Move-assign a basic_file from a file of another executor type.
/**
* This assignment operator moves a file from one object to another.
*
* @param other The other basic_file object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_file(const executor_type&) constructor.
*/
template <typename Executor1>
constraint_t<
is_convertible<Executor1, Executor>::value,
basic_file&
> operator=(basic_file<Executor1>&& other)
{
basic_file tmp(std::move(other));
impl_ = std::move(tmp.impl_);
return *this;
}
/// Get the executor associated with the object.
const executor_type& get_executor() noexcept
{
return impl_.get_executor();
}
/// Open the file using the specified path.
/**
* This function opens the file so that it will use the specified path.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* @code
* asio::stream_file file(my_context);
* file.open("/path/to/my/file", asio::stream_file::read_only);
* @endcode
*/
void open(const char* path, file_base::flags open_flags)
{
asio::error_code ec;
impl_.get_service().open(impl_.get_implementation(), path, open_flags, ec);
asio::detail::throw_error(ec, "open");
}
/// Open the file using the specified path.
/**
* This function opens the file so that it will use the specified path.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @param ec Set to indicate what error occurred, if any.
*
* @par Example
* @code
* asio::stream_file file(my_context);
* asio::error_code ec;
* file.open("/path/to/my/file", asio::stream_file::read_only, ec);
* if (ec)
* {
* // An error occurred.
* }
* @endcode
*/
ASIO_SYNC_OP_VOID open(const char* path,
file_base::flags open_flags, asio::error_code& ec)
{
impl_.get_service().open(impl_.get_implementation(), path, open_flags, ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Open the file using the specified path.
/**
* This function opens the file so that it will use the specified path.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* @code
* asio::stream_file file(my_context);
* file.open("/path/to/my/file", asio::stream_file::read_only);
* @endcode
*/
void open(const std::string& path, file_base::flags open_flags)
{
asio::error_code ec;
impl_.get_service().open(impl_.get_implementation(),
path.c_str(), open_flags, ec);
asio::detail::throw_error(ec, "open");
}
/// Open the file using the specified path.
/**
* This function opens the file so that it will use the specified path.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @param ec Set to indicate what error occurred, if any.
*
* @par Example
* @code
* asio::stream_file file(my_context);
* asio::error_code ec;
* file.open("/path/to/my/file", asio::stream_file::read_only, ec);
* if (ec)
* {
* // An error occurred.
* }
* @endcode
*/
ASIO_SYNC_OP_VOID open(const std::string& path,
file_base::flags open_flags, asio::error_code& ec)
{
impl_.get_service().open(impl_.get_implementation(),
path.c_str(), open_flags, ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Assign an existing native file to the file.
/*
* This function opens the file to hold an existing native file.
*
* @param native_file A native file.
*
* @throws asio::system_error Thrown on failure.
*/
void assign(const native_handle_type& native_file)
{
asio::error_code ec;
impl_.get_service().assign(
impl_.get_implementation(), native_file, ec);
asio::detail::throw_error(ec, "assign");
}
/// Assign an existing native file to the file.
/*
* This function opens the file to hold an existing native file.
*
* @param native_file A native file.
*
* @param ec Set to indicate what error occurred, if any.
*/
ASIO_SYNC_OP_VOID assign(const native_handle_type& native_file,
asio::error_code& ec)
{
impl_.get_service().assign(
impl_.get_implementation(), native_file, ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Determine whether the file is open.
bool is_open() const
{
return impl_.get_service().is_open(impl_.get_implementation());
}
/// Close the file.
/**
* This function is used to close the file. Any asynchronous read or write
* operations will be cancelled immediately, and will complete with the
* asio::error::operation_aborted error.
*
* @throws asio::system_error Thrown on failure. Note that, even if
* the function indicates an error, the underlying descriptor is closed.
*/
void close()
{
asio::error_code ec;
impl_.get_service().close(impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "close");
}
/// Close the file.
/**
* This function is used to close the file. Any asynchronous read or write
* operations will be cancelled immediately, and will complete with the
* asio::error::operation_aborted error.
*
* @param ec Set to indicate what error occurred, if any. Note that, even if
* the function indicates an error, the underlying descriptor is closed.
*
* @par Example
* @code
* asio::stream_file file(my_context);
* ...
* asio::error_code ec;
* file.close(ec);
* if (ec)
* {
* // An error occurred.
* }
* @endcode
*/
ASIO_SYNC_OP_VOID close(asio::error_code& ec)
{
impl_.get_service().close(impl_.get_implementation(), ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying native file.
/**
* This function causes all outstanding asynchronous read and write
* operations to finish immediately, and the handlers for cancelled
* operations will be passed the asio::error::operation_aborted error.
* Ownership of the native file is then transferred to the caller.
*
* @throws asio::system_error Thrown on failure.
*
* @note This function is unsupported on Windows versions prior to Windows
* 8.1, and will fail with asio::error::operation_not_supported on
* these platforms.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
__declspec(deprecated("This function always fails with "
"operation_not_supported when used on Windows versions "
"prior to Windows 8.1."))
#endif
native_handle_type release()
{
asio::error_code ec;
native_handle_type s = impl_.get_service().release(
impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "release");
return s;
}
/// Release ownership of the underlying native file.
/**
* This function causes all outstanding asynchronous read and write
* operations to finish immediately, and the handlers for cancelled
* operations will be passed the asio::error::operation_aborted error.
* Ownership of the native file is then transferred to the caller.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note This function is unsupported on Windows versions prior to Windows
* 8.1, and will fail with asio::error::operation_not_supported on
* these platforms.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
__declspec(deprecated("This function always fails with "
"operation_not_supported when used on Windows versions "
"prior to Windows 8.1."))
#endif
native_handle_type release(asio::error_code& ec)
{
return impl_.get_service().release(impl_.get_implementation(), ec);
}
/// Get the native file representation.
/**
* This function may be used to obtain the underlying representation of the
* file. This is intended to allow access to native file functionality
* that is not otherwise provided.
*/
native_handle_type native_handle()
{
return impl_.get_service().native_handle(impl_.get_implementation());
}
/// Cancel all asynchronous operations associated with the file.
/**
* This function causes all outstanding asynchronous read and write
* operations to finish immediately, and the handlers for cancelled
* operations will be passed the asio::error::operation_aborted error.
*
* @throws asio::system_error Thrown on failure.
*
* @note Calls to cancel() will always fail with
* asio::error::operation_not_supported when run on Windows XP, Windows
* Server 2003, and earlier versions of Windows, unless
* ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has
* two issues that should be considered before enabling its use:
*
* @li It will only cancel asynchronous operations that were initiated in the
* current thread.
*
* @li It can appear to complete without error, but the request to cancel the
* unfinished operations may be silently ignored by the operating system.
* Whether it works or not seems to depend on the drivers that are installed.
*
* For portable cancellation, consider using the close() function to
* simultaneously cancel the outstanding operations and close the file.
*
* When running on Windows Vista, Windows Server 2008, and later, the
* CancelIoEx function is always used. This function does not have the
* problems described above.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \
&& !defined(ASIO_ENABLE_CANCELIO)
__declspec(deprecated("By default, this function always fails with "
"operation_not_supported when used on Windows XP, Windows Server 2003, "
"or earlier. Consult documentation for details."))
#endif
void cancel()
{
asio::error_code ec;
impl_.get_service().cancel(impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "cancel");
}
/// Cancel all asynchronous operations associated with the file.
/**
* This function causes all outstanding asynchronous read and write
* operations to finish immediately, and the handlers for cancelled
* operations will be passed the asio::error::operation_aborted error.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note Calls to cancel() will always fail with
* asio::error::operation_not_supported when run on Windows XP, Windows
* Server 2003, and earlier versions of Windows, unless
* ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has
* two issues that should be considered before enabling its use:
*
* @li It will only cancel asynchronous operations that were initiated in the
* current thread.
*
* @li It can appear to complete without error, but the request to cancel the
* unfinished operations may be silently ignored by the operating system.
* Whether it works or not seems to depend on the drivers that are installed.
*
* For portable cancellation, consider using the close() function to
* simultaneously cancel the outstanding operations and close the file.
*
* When running on Windows Vista, Windows Server 2008, and later, the
* CancelIoEx function is always used. This function does not have the
* problems described above.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \
&& !defined(ASIO_ENABLE_CANCELIO)
__declspec(deprecated("By default, this function always fails with "
"operation_not_supported when used on Windows XP, Windows Server 2003, "
"or earlier. Consult documentation for details."))
#endif
ASIO_SYNC_OP_VOID cancel(asio::error_code& ec)
{
impl_.get_service().cancel(impl_.get_implementation(), ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Get the size of the file.
/**
* This function determines the size of the file, in bytes.
*
* @throws asio::system_error Thrown on failure.
*/
uint64_t size() const
{
asio::error_code ec;
uint64_t s = impl_.get_service().size(impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "size");
return s;
}
/// Get the size of the file.
/**
* This function determines the size of the file, in bytes.
*
* @param ec Set to indicate what error occurred, if any.
*/
uint64_t size(asio::error_code& ec) const
{
return impl_.get_service().size(impl_.get_implementation(), ec);
}
/// Alter the size of the file.
/**
* This function resizes the file to the specified size, in bytes. If the
* current file size exceeds @c n then any extra data is discarded. If the
* current size is less than @c n then the file is extended and filled with
* zeroes.
*
* @param n The new size for the file.
*
* @throws asio::system_error Thrown on failure.
*/
void resize(uint64_t n)
{
asio::error_code ec;
impl_.get_service().resize(impl_.get_implementation(), n, ec);
asio::detail::throw_error(ec, "resize");
}
/// Alter the size of the file.
/**
* This function resizes the file to the specified size, in bytes. If the
* current file size exceeds @c n then any extra data is discarded. If the
* current size is less than @c n then the file is extended and filled with
* zeroes.
*
* @param n The new size for the file.
*
* @param ec Set to indicate what error occurred, if any.
*/
ASIO_SYNC_OP_VOID resize(uint64_t n, asio::error_code& ec)
{
impl_.get_service().resize(impl_.get_implementation(), n, ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Synchronise the file to disk.
/**
* This function synchronises the file data and metadata to disk. Note that
* the semantics of this synchronisation vary between operation systems.
*
* @throws asio::system_error Thrown on failure.
*/
void sync_all()
{
asio::error_code ec;
impl_.get_service().sync_all(impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "sync_all");
}
/// Synchronise the file to disk.
/**
* This function synchronises the file data and metadata to disk. Note that
* the semantics of this synchronisation vary between operation systems.
*
* @param ec Set to indicate what error occurred, if any.
*/
ASIO_SYNC_OP_VOID sync_all(asio::error_code& ec)
{
impl_.get_service().sync_all(impl_.get_implementation(), ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Synchronise the file data to disk.
/**
* This function synchronises the file data to disk. Note that the semantics
* of this synchronisation vary between operation systems.
*
* @throws asio::system_error Thrown on failure.
*/
void sync_data()
{
asio::error_code ec;
impl_.get_service().sync_data(impl_.get_implementation(), ec);
asio::detail::throw_error(ec, "sync_data");
}
/// Synchronise the file data to disk.
/**
* This function synchronises the file data to disk. Note that the semantics
* of this synchronisation vary between operation systems.
*
* @param ec Set to indicate what error occurred, if any.
*/
ASIO_SYNC_OP_VOID sync_data(asio::error_code& ec)
{
impl_.get_service().sync_data(impl_.get_implementation(), ec);
ASIO_SYNC_OP_VOID_RETURN(ec);
}
protected:
/// Protected destructor to prevent deletion through this type.
/**
* This function destroys the file, cancelling any outstanding asynchronous
* operations associated with the file as if by calling @c cancel.
*/
~basic_file()
{
}
#if defined(ASIO_HAS_IOCP)
detail::io_object_impl<detail::win_iocp_file_service, Executor> impl_;
#elif defined(ASIO_HAS_IO_URING)
detail::io_object_impl<detail::io_uring_file_service, Executor> impl_;
#endif
private:
// Disallow copying and assignment.
basic_file(const basic_file&) = delete;
basic_file& operator=(const basic_file&) = delete;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // defined(ASIO_HAS_FILE)
// || defined(GENERATING_DOCUMENTATION)
#endif // ASIO_BASIC_FILE_HPP

View File

@@ -1,286 +0,0 @@
//
// basic_io_object.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_IO_OBJECT_HPP
#define ASIO_BASIC_IO_OBJECT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/io_context.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail
{
// Type trait used to determine whether a service supports move.
template <typename IoObjectService>
class service_has_move
{
private:
typedef IoObjectService service_type;
typedef typename service_type::implementation_type implementation_type;
template <typename T, typename U>
static auto asio_service_has_move_eval(T* t, U* u)
-> decltype(t->move_construct(*u, *u), char());
static char (&asio_service_has_move_eval(...))[2];
public:
static const bool value =
sizeof(asio_service_has_move_eval(
static_cast<service_type*>(0),
static_cast<implementation_type*>(0))) == 1;
};
}
/// Base class for all I/O objects.
/**
* @note All I/O objects are non-copyable. However, when using C++0x, certain
* I/O objects do support move construction and move assignment.
*/
#if defined(GENERATING_DOCUMENTATION)
template <typename IoObjectService>
#else
template <typename IoObjectService,
bool Movable = detail::service_has_move<IoObjectService>::value>
#endif
class basic_io_object
{
public:
/// The type of the service that will be used to provide I/O operations.
typedef IoObjectService service_type;
/// The underlying implementation type of I/O object.
typedef typename service_type::implementation_type implementation_type;
#if !defined(ASIO_NO_DEPRECATED)
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
/**
* This function may be used to obtain the io_context object that the I/O
* object uses to dispatch handlers for asynchronous operations.
*
* @return A reference to the io_context object that the I/O object will use
* to dispatch handlers. Ownership is not transferred to the caller.
*/
asio::io_context& get_io_context()
{
return service_.get_io_context();
}
/// (Deprecated: Use get_executor().) Get the io_context associated with the
/// object.
/**
* This function may be used to obtain the io_context object that the I/O
* object uses to dispatch handlers for asynchronous operations.
*
* @return A reference to the io_context object that the I/O object will use
* to dispatch handlers. Ownership is not transferred to the caller.
*/
asio::io_context& get_io_service()
{
return service_.get_io_context();
}
#endif // !defined(ASIO_NO_DEPRECATED)
/// The type of the executor associated with the object.
typedef asio::io_context::executor_type executor_type;
/// Get the executor associated with the object.
executor_type get_executor() noexcept
{
return service_.get_io_context().get_executor();
}
protected:
/// Construct a basic_io_object.
/**
* Performs:
* @code get_service().construct(get_implementation()); @endcode
*/
explicit basic_io_object(asio::io_context& io_context)
: service_(asio::use_service<IoObjectService>(io_context))
{
service_.construct(implementation_);
}
#if defined(GENERATING_DOCUMENTATION)
/// Move-construct a basic_io_object.
/**
* Performs:
* @code get_service().move_construct(
* get_implementation(), other.get_implementation()); @endcode
*
* @note Available only for services that support movability,
*/
basic_io_object(basic_io_object&& other);
/// Move-assign a basic_io_object.
/**
* Performs:
* @code get_service().move_assign(get_implementation(),
* other.get_service(), other.get_implementation()); @endcode
*
* @note Available only for services that support movability,
*/
basic_io_object& operator=(basic_io_object&& other);
/// Perform a converting move-construction of a basic_io_object.
template <typename IoObjectService1>
basic_io_object(IoObjectService1& other_service,
typename IoObjectService1::implementation_type& other_implementation);
#endif // defined(GENERATING_DOCUMENTATION)
/// Protected destructor to prevent deletion through this type.
/**
* Performs:
* @code get_service().destroy(get_implementation()); @endcode
*/
~basic_io_object()
{
service_.destroy(implementation_);
}
/// Get the service associated with the I/O object.
service_type& get_service()
{
return service_;
}
/// Get the service associated with the I/O object.
const service_type& get_service() const
{
return service_;
}
/// Get the underlying implementation of the I/O object.
implementation_type& get_implementation()
{
return implementation_;
}
/// Get the underlying implementation of the I/O object.
const implementation_type& get_implementation() const
{
return implementation_;
}
private:
basic_io_object(const basic_io_object&);
basic_io_object& operator=(const basic_io_object&);
// The service associated with the I/O object.
service_type& service_;
/// The underlying implementation of the I/O object.
implementation_type implementation_;
};
// Specialisation for movable objects.
template <typename IoObjectService>
class basic_io_object<IoObjectService, true>
{
public:
typedef IoObjectService service_type;
typedef typename service_type::implementation_type implementation_type;
#if !defined(ASIO_NO_DEPRECATED)
asio::io_context& get_io_context()
{
return service_->get_io_context();
}
asio::io_context& get_io_service()
{
return service_->get_io_context();
}
#endif // !defined(ASIO_NO_DEPRECATED)
typedef asio::io_context::executor_type executor_type;
executor_type get_executor() noexcept
{
return service_->get_io_context().get_executor();
}
protected:
explicit basic_io_object(asio::io_context& io_context)
: service_(&asio::use_service<IoObjectService>(io_context))
{
service_->construct(implementation_);
}
basic_io_object(basic_io_object&& other)
: service_(&other.get_service())
{
service_->move_construct(implementation_, other.implementation_);
}
template <typename IoObjectService1>
basic_io_object(IoObjectService1& other_service,
typename IoObjectService1::implementation_type& other_implementation)
: service_(&asio::use_service<IoObjectService>(
other_service.get_io_context()))
{
service_->converting_move_construct(implementation_,
other_service, other_implementation);
}
~basic_io_object()
{
service_->destroy(implementation_);
}
basic_io_object& operator=(basic_io_object&& other)
{
service_->move_assign(implementation_,
*other.service_, other.implementation_);
service_ = other.service_;
return *this;
}
service_type& get_service()
{
return *service_;
}
const service_type& get_service() const
{
return *service_;
}
implementation_type& get_implementation()
{
return implementation_;
}
const implementation_type& get_implementation() const
{
return implementation_;
}
private:
basic_io_object(const basic_io_object&);
void operator=(const basic_io_object&);
IoObjectService* service_;
implementation_type implementation_;
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_BASIC_IO_OBJECT_HPP

View File

@@ -1,689 +0,0 @@
//
// basic_random_access_file.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_BASIC_RANDOM_ACCESS_FILE_HPP
#define ASIO_BASIC_RANDOM_ACCESS_FILE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_FILE) \
|| defined(GENERATING_DOCUMENTATION)
#include <cstddef>
#include "asio/async_result.hpp"
#include "asio/basic_file.hpp"
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/non_const_lvalue.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
#if !defined(ASIO_BASIC_RANDOM_ACCESS_FILE_FWD_DECL)
#define ASIO_BASIC_RANDOM_ACCESS_FILE_FWD_DECL
// Forward declaration with defaulted arguments.
template <typename Executor = any_io_executor>
class basic_random_access_file;
#endif // !defined(ASIO_BASIC_RANDOM_ACCESS_FILE_FWD_DECL)
/// Provides random-access file functionality.
/**
* The basic_random_access_file class template provides asynchronous and
* blocking random-access file functionality.
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* Synchronous @c read_some_at and @c write_some_at operations are thread safe
* with respect to each other, if the underlying operating system calls are
* also thread safe. This means that it is permitted to perform concurrent
* calls to these synchronous operations on a single file object. Other
* synchronous operations, such as @c open or @c close, are not thread safe.
*/
template <typename Executor>
class basic_random_access_file
: public basic_file<Executor>
{
private:
class initiate_async_write_some_at;
class initiate_async_read_some_at;
public:
/// The type of the executor associated with the object.
typedef Executor executor_type;
/// Rebinds the file type to another executor.
template <typename Executor1>
struct rebind_executor
{
/// The file type when rebound to the specified executor.
typedef basic_random_access_file<Executor1> other;
};
/// The native representation of a file.
#if defined(GENERATING_DOCUMENTATION)
typedef implementation_defined native_handle_type;
#else
typedef typename basic_file<Executor>::native_handle_type native_handle_type;
#endif
/// Construct a basic_random_access_file without opening it.
/**
* This constructor initialises a file without opening it. The file needs to
* be opened before data can be read from or or written to it.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*/
explicit basic_random_access_file(const executor_type& ex)
: basic_file<Executor>(ex)
{
}
/// Construct a basic_random_access_file without opening it.
/**
* This constructor initialises a file without opening it. The file needs to
* be opened before data can be read from or or written to it.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*/
template <typename ExecutionContext>
explicit basic_random_access_file(ExecutionContext& context,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: basic_file<Executor>(context)
{
}
/// Construct and open a basic_random_access_file.
/**
* This constructor initialises and opens a file.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @throws asio::system_error Thrown on failure.
*/
basic_random_access_file(const executor_type& ex,
const char* path, file_base::flags open_flags)
: basic_file<Executor>(ex, path, open_flags)
{
}
/// Construct and open a basic_random_access_file.
/**
* This constructor initialises and opens a file.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename ExecutionContext>
basic_random_access_file(ExecutionContext& context,
const char* path, file_base::flags open_flags,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: basic_file<Executor>(context, path, open_flags)
{
}
/// Construct and open a basic_random_access_file.
/**
* This constructor initialises and opens a file.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @throws asio::system_error Thrown on failure.
*/
basic_random_access_file(const executor_type& ex,
const std::string& path, file_base::flags open_flags)
: basic_file<Executor>(ex, path, open_flags)
{
}
/// Construct and open a basic_random_access_file.
/**
* This constructor initialises and opens a file.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*
* @param path The path name identifying the file to be opened.
*
* @param open_flags A set of flags that determine how the file should be
* opened.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename ExecutionContext>
basic_random_access_file(ExecutionContext& context,
const std::string& path, file_base::flags open_flags,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: basic_file<Executor>(context, path, open_flags)
{
}
/// Construct a basic_random_access_file on an existing native file.
/**
* This constructor initialises a random-access file object to hold an
* existing native file.
*
* @param ex The I/O executor that the file will use, by default, to
* dispatch handlers for any asynchronous operations performed on the file.
*
* @param native_file The new underlying file implementation.
*
* @throws asio::system_error Thrown on failure.
*/
basic_random_access_file(const executor_type& ex,
const native_handle_type& native_file)
: basic_file<Executor>(ex, native_file)
{
}
/// Construct a basic_random_access_file on an existing native file.
/**
* This constructor initialises a random-access file object to hold an
* existing native file.
*
* @param context An execution context which provides the I/O executor that
* the file will use, by default, to dispatch handlers for any asynchronous
* operations performed on the file.
*
* @param native_file The new underlying file implementation.
*
* @throws asio::system_error Thrown on failure.
*/
template <typename ExecutionContext>
basic_random_access_file(ExecutionContext& context,
const native_handle_type& native_file,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: basic_file<Executor>(context, native_file)
{
}
/// Move-construct a basic_random_access_file from another.
/**
* This constructor moves a random-access file from one object to another.
*
* @param other The other basic_random_access_file object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_random_access_file(const executor_type&)
* constructor.
*/
basic_random_access_file(basic_random_access_file&& other) noexcept
: basic_file<Executor>(std::move(other))
{
}
/// Move-assign a basic_random_access_file from another.
/**
* This assignment operator moves a random-access file from one object to
* another.
*
* @param other The other basic_random_access_file object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_random_access_file(const executor_type&)
* constructor.
*/
basic_random_access_file& operator=(basic_random_access_file&& other)
{
basic_file<Executor>::operator=(std::move(other));
return *this;
}
/// Move-construct a basic_random_access_file from a file of another executor
/// type.
/**
* This constructor moves a random-access file from one object to another.
*
* @param other The other basic_random_access_file object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_random_access_file(const executor_type&)
* constructor.
*/
template <typename Executor1>
basic_random_access_file(basic_random_access_file<Executor1>&& other,
constraint_t<
is_convertible<Executor1, Executor>::value,
defaulted_constraint
> = defaulted_constraint())
: basic_file<Executor>(std::move(other))
{
}
/// Move-assign a basic_random_access_file from a file of another executor
/// type.
/**
* This assignment operator moves a random-access file from one object to
* another.
*
* @param other The other basic_random_access_file object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_random_access_file(const executor_type&)
* constructor.
*/
template <typename Executor1>
constraint_t<
is_convertible<Executor1, Executor>::value,
basic_random_access_file&
> operator=(basic_random_access_file<Executor1>&& other)
{
basic_file<Executor>::operator=(std::move(other));
return *this;
}
/// Destroys the file.
/**
* This function destroys the file, cancelling any outstanding asynchronous
* operations associated with the file as if by calling @c cancel.
*/
~basic_random_access_file()
{
}
/// Write some data to the handle at the specified offset.
/**
* This function is used to write data to the random-access handle. The
* function call will block until one or more bytes of the data has been
* written successfully, or until an error occurs.
*
* @param offset The offset at which the data will be written.
*
* @param buffers One or more data buffers to be written to the handle.
*
* @returns The number of bytes written.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the end of the file was reached.
*
* @note The write_some_at operation may not write all of the data. Consider
* using the @ref write_at function if you need to ensure that all data is
* written before the blocking operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* handle.write_some_at(42, asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t write_some_at(uint64_t offset,
const ConstBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->impl_.get_service().write_some_at(
this->impl_.get_implementation(), offset, buffers, ec);
asio::detail::throw_error(ec, "write_some_at");
return s;
}
/// Write some data to the handle at the specified offset.
/**
* This function is used to write data to the random-access handle. The
* function call will block until one or more bytes of the data has been
* written successfully, or until an error occurs.
*
* @param offset The offset at which the data will be written.
*
* @param buffers One or more data buffers to be written to the handle.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes written. Returns 0 if an error occurred.
*
* @note The write_some operation may not write all of the data to the
* file. Consider using the @ref write_at function if you need to ensure that
* all data is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t write_some_at(uint64_t offset,
const ConstBufferSequence& buffers, asio::error_code& ec)
{
return this->impl_.get_service().write_some_at(
this->impl_.get_implementation(), offset, buffers, ec);
}
/// Start an asynchronous write at the specified offset.
/**
* This function is used to asynchronously write data to the random-access
* handle. It is an initiating function for an @ref asynchronous_operation,
* and always returns immediately.
*
* @param offset The offset at which the data will be written.
*
* @param buffers One or more data buffers to be written to the handle.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the completion handler is called.
*
* @param token The @ref completion_token that will be used to produce a
* completion handler, which will be called when the write completes.
* Potential completion tokens include @ref use_future, @ref use_awaitable,
* @ref yield_context, or a function object with the correct completion
* signature. The function signature of the completion handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes written.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the completion handler will not be invoked from within this function.
* On immediate completion, invocation of the handler will be performed in a
* manner equivalent to using asio::async_immediate().
*
* @par Completion Signature
* @code void(asio::error_code, std::size_t) @endcode
*
* @note The write operation may not write all of the data to the file.
* Consider using the @ref async_write_at function if you need to ensure that
* all data is written before the asynchronous operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* handle.async_write_some_at(42, asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*
* @par Per-Operation Cancellation
* This asynchronous operation supports cancellation for the following
* asio::cancellation_type values:
*
* @li @c cancellation_type::terminal
*
* @li @c cancellation_type::partial
*
* @li @c cancellation_type::total
*/
template <typename ConstBufferSequence,
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code,
std::size_t)) WriteToken = default_completion_token_t<executor_type>>
auto async_write_some_at(uint64_t offset, const ConstBufferSequence& buffers,
WriteToken&& token = default_completion_token_t<executor_type>())
-> decltype(
async_initiate<WriteToken,
void (asio::error_code, std::size_t)>(
declval<initiate_async_write_some_at>(), token, offset, buffers))
{
return async_initiate<WriteToken,
void (asio::error_code, std::size_t)>(
initiate_async_write_some_at(this), token, offset, buffers);
}
/// Read some data from the handle at the specified offset.
/**
* This function is used to read data from the random-access handle. The
* function call will block until one or more bytes of data has been read
* successfully, or until an error occurs.
*
* @param offset The offset at which the data will be read.
*
* @param buffers One or more buffers into which the data will be read.
*
* @returns The number of bytes read.
*
* @throws asio::system_error Thrown on failure. An error code of
* asio::error::eof indicates that the end of the file was reached.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read_at function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* handle.read_some_at(42, asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t read_some_at(uint64_t offset,
const MutableBufferSequence& buffers)
{
asio::error_code ec;
std::size_t s = this->impl_.get_service().read_some_at(
this->impl_.get_implementation(), offset, buffers, ec);
asio::detail::throw_error(ec, "read_some_at");
return s;
}
/// Read some data from the handle at the specified offset.
/**
* This function is used to read data from the random-access handle. The
* function call will block until one or more bytes of data has been read
* successfully, or until an error occurs.
*
* @param offset The offset at which the data will be read.
*
* @param buffers One or more buffers into which the data will be read.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes read. Returns 0 if an error occurred.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read_at function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*/
template <typename MutableBufferSequence>
std::size_t read_some_at(uint64_t offset,
const MutableBufferSequence& buffers, asio::error_code& ec)
{
return this->impl_.get_service().read_some_at(
this->impl_.get_implementation(), offset, buffers, ec);
}
/// Start an asynchronous read at the specified offset.
/**
* This function is used to asynchronously read data from the random-access
* handle. It is an initiating function for an @ref asynchronous_operation,
* and always returns immediately.
*
* @param offset The offset at which the data will be read.
*
* @param buffers One or more buffers into which the data will be read.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the completion handler is called.
*
* @param token The @ref completion_token that will be used to produce a
* completion handler, which will be called when the read completes.
* Potential completion tokens include @ref use_future, @ref use_awaitable,
* @ref yield_context, or a function object with the correct completion
* signature. The function signature of the completion handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes read.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the completion handler will not be invoked from within this function.
* On immediate completion, invocation of the handler will be performed in a
* manner equivalent to using asio::async_immediate().
*
* @par Completion Signature
* @code void(asio::error_code, std::size_t) @endcode
*
* @note The read operation may not read all of the requested number of bytes.
* Consider using the @ref async_read_at function if you need to ensure that
* the requested amount of data is read before the asynchronous operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* handle.async_read_some_at(42, asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*
* @par Per-Operation Cancellation
* This asynchronous operation supports cancellation for the following
* asio::cancellation_type values:
*
* @li @c cancellation_type::terminal
*
* @li @c cancellation_type::partial
*
* @li @c cancellation_type::total
*/
template <typename MutableBufferSequence,
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code,
std::size_t)) ReadToken = default_completion_token_t<executor_type>>
auto async_read_some_at(uint64_t offset, const MutableBufferSequence& buffers,
ReadToken&& token = default_completion_token_t<executor_type>())
-> decltype(
async_initiate<ReadToken,
void (asio::error_code, std::size_t)>(
declval<initiate_async_read_some_at>(), token, offset, buffers))
{
return async_initiate<ReadToken,
void (asio::error_code, std::size_t)>(
initiate_async_read_some_at(this), token, offset, buffers);
}
private:
// Disallow copying and assignment.
basic_random_access_file(const basic_random_access_file&) = delete;
basic_random_access_file& operator=(
const basic_random_access_file&) = delete;
class initiate_async_write_some_at
{
public:
typedef Executor executor_type;
explicit initiate_async_write_some_at(basic_random_access_file* self)
: self_(self)
{
}
const executor_type& get_executor() const noexcept
{
return self_->get_executor();
}
template <typename WriteHandler, typename ConstBufferSequence>
void operator()(WriteHandler&& handler,
uint64_t offset, const ConstBufferSequence& buffers) const
{
// If you get an error on the following line it means that your handler
// does not meet the documented type requirements for a WriteHandler.
ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
detail::non_const_lvalue<WriteHandler> handler2(handler);
self_->impl_.get_service().async_write_some_at(
self_->impl_.get_implementation(), offset, buffers,
handler2.value, self_->impl_.get_executor());
}
private:
basic_random_access_file* self_;
};
class initiate_async_read_some_at
{
public:
typedef Executor executor_type;
explicit initiate_async_read_some_at(basic_random_access_file* self)
: self_(self)
{
}
const executor_type& get_executor() const noexcept
{
return self_->get_executor();
}
template <typename ReadHandler, typename MutableBufferSequence>
void operator()(ReadHandler&& handler,
uint64_t offset, const MutableBufferSequence& buffers) const
{
// If you get an error on the following line it means that your handler
// does not meet the documented type requirements for a ReadHandler.
ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
detail::non_const_lvalue<ReadHandler> handler2(handler);
self_->impl_.get_service().async_read_some_at(
self_->impl_.get_implementation(), offset, buffers,
handler2.value, self_->impl_.get_executor());
}
private:
basic_random_access_file* self_;
};
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // defined(ASIO_HAS_FILE)
// || defined(GENERATING_DOCUMENTATION)
#endif // ASIO_BASIC_RANDOM_ACCESS_FILE_HPP

Some files were not shown because too many files have changed in this diff Show More