栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

学习P4的C++API的基础操作

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

学习P4的C++API的基础操作

前情提要&本篇目标

在《学习从零创建并配置一个P4服务器》中我尝试配置了一个P4服务器,这将是最近几次P4相关实验的环境。
在上一篇《尝试配置P4的C++API运行环境》中我已经处理好了P4的C++API的基础运行环境。
本篇将是接着当前的环境,继续实验来学习P4的C++API的基础操作。
(代码参考了很多UE引擎的代码,见 PerforceConnection.cpp)

基础

在上一篇中环境已经配置好了,这是当前最基础的代码:

#include 

#include "p4/clientapi.h"
#include "p4/p4libs.h"

int main()
{
	ClientApi client;	//调用API的结构
	Error e;			//用于容纳错误信息的结构

	//初始化库
	P4Libraries::Initialize(P4LIBRARIES_INIT_ALL, &e);
	//初始化API
	client.Init(&e);

	//此处为具体逻辑
	{
		//默认的ClientUser:
		ClientUser ui;		
		//测试运行info命令
		client.Run("info", &ui);
	}	

	//API结束
	client.Final(&e);
	//库关闭
	P4Libraries::Shutdown(P4LIBRARIES_INIT_ALL, &e);

	return 0;
}

这将输出:

User name: yaksueadmin
Client name: DESKTOP-O4QC2J1
Client host: DESKTOP-O4QC2J1
Client unknown.
Current directory: d:_WorkSpaceCppTestP4Cpp
Peer address: 127.0.0.1:50262
Client address: 127.0.0.1
Server address: DESKTOP-O4QC2J1:1666
Server root: C:PerforceRoot
Server date: 2022/05/29 10:38:45 +0800
Server uptime: 19:01:48
Server version: P4D/NTX64/2022.1/2285021 (2022/05/13)
Server license: none
Case Handling: insensitive

忽略掉开始的初始化代码以及结尾的代码后,其实具体的逻辑只是调用了一个info命令

//此处为具体逻辑
{
	//默认的ClientUser:
	ClientUser ui;		
	//测试运行info命令
	client.Run("info", &ui);
}	

正如 官方文档所记录的API架构 所描述:

The client session is managed by a C++ class called ClientApi. (See ClientApi methods)
User interaction is channeled through the ClientUser C++ class. The default methods of ClientUser implement the p4 command line interface. To create a custom client application, create a subclasses of ClientUser. (See ClientUser methods)
客户端会话由名为 ClientApi 的 C++ 类管理。 (参见 ClientApi )
用户交互是通过 ClientUser 这个 C++ 类进行的。 ClientUser 的默认方法就是 p4 的命令行界面。 而要创建自定义的客户端应用程序,就要创建 ClientUser 的子类。 (参见 ClientUser )

也就是说,如果要实现更复杂的逻辑需要注意两方面:

  • 调用 ClientApi 的函数。
  • 实现自定义的 ClientUser 。
1. 得到标签化的输出状态(学习OutputStat)

ClientUser::OutputStat( StrDict * ) 是在处理(标签化的)输出时调用的函数,可以override这个函数以实现自己的逻辑:比如存储到变量中,以便后续访问:

#include 
//定义一个自己的ClientUser类
class MyClientUser : public ClientUser
{
public:
	//存储返回的数据:
	std::vector> data;

	//会被P4API处理标签化的输出时调用:
	virtual void OutputStat(StrDict* varList) override
	{
		StrRef Var, Value;
		//添加所有的键值对:
		for (int Index = 0; varList->GetVar(Index, Var, Value); Index++)
		{
			std::string tagName = Var.Text();
			std::string tagValue = Value.Text();
			data.push_back(std::make_pair(tagName, tagValue));
		}
	}
};

然后修改一下main中的逻辑:

//此处为具体逻辑
{
	//创建自定义的ClientUser:
	MyClientUser cu;
	//设置标签化:
	client.SetProtocol("tag", "");
	//测试运行info命令
	client.Run("info", &cu);

	//测试打印出结果:
	for (auto pair : cu.data)
	{
		std::cout << "Key:   " << pair.first << std::endl;
		std::cout << "Value: " << pair.second << std::endl;
		std::cout << std::endl;
	}
}	

这将会输出:

Key:   userName
Value: yaksueadmin

Key:   password
Value: enabled

Key:   clientName
Value: *unknown*

Key:   clientCwd
Value: d:_WorkSpaceCppTestP4Cpp

Key:   clientHost
Value: DESKTOP-O4QC2J1

Key:   clientCase
Value: insensitive

Key:   peerAddress
Value: 127.0.0.1:61628

Key:   clientAddress
Value: 127.0.0.1

Key:   serverName
Value: Perforce

Key:   security
Value: enabled

Key:   serverAddress
Value: DESKTOP-O4QC2J1:1666

Key:   serverRoot
Value: C:PerforceRoot

Key:   serverDate
Value: 2022/05/29 12:13:57 +0800

Key:   tzoffset
Value: 28800

Key:   serverUptime
Value: 20:37:00

Key:   serverVersion
Value: P4D/NTX64/2022.1/2285021 (2022/05/13)

Key:   serverServices
Value: standard

Key:   serverLicense
Value: none

Key:   caseHandling
Value: insensitive

Key:   allowStreamSpecInteg
Value: 1

Key:   parentViewDefault
Value: inheritAll

Key:   unloadSupport
Value: disabled

Key:   extensionsSupport
Value: enabled

Key:   memoryManager
Value: mimalloc 173

Key:   func
Value: client-FstatInfo

看来输出确实都存到了 MyClientUser 的 data 中。
后续调用其他命令也可以从 data 中找到自己所感兴趣的那一项的值从而进行处理。

2. 登录(学习Prompt)

经过之前在P4服务器中的设置,所有用户在登录时,必须输入密码。

而这个可以由 ClientUser::Prompt( const StrPtr &, StrBuf &, int, Error * ) 来指定:

//定义一个用来登录的ClientUser类
class ClientUserForLogin : public ClientUser
{
public:
	std::string password; //用于登录的密码

	//会被P4API调用来得到用户输入
	virtual void Prompt(const StrPtr& InMessage, StrBuf& OutPrompt, int NoEcho, Error* InError) override
	{
		OutPrompt.Set(password.c_str());
	}
};

现在main中的逻辑修改为:

//此处为具体逻辑
{
	//登录:
	{
		//设置用户名:
		client.SetUser("yaksue");

		//创建个用来登录的ClientUser并指定密码:
		ClientUserForLogin cu_login;
		cu_login.password = 此处填写用户的密码!;

		//设置login命令的参数:
		const char* ArgV[] = { "-a" };
		client.SetArgv(1, const_cast(ArgV));
		//运行login命令:
		client.Run("login", &cu_login);
	}
}

输出:

User yaksue logged in.
3. 获得输出结果(学习OutputInfo)
//定义用来获取输出的ClientUser类
class ClientUserForOutput : public ClientUser
{
public:
	//存储命令执行的结果:
	std::vector results; 

	//P4命令输出结果时调用:
	void OutputInfo(char level, const char* data) override
	{
		results.push_back(data);
	}
};

下面测试使用clients命令获得WorkSpace列表。(注意,此命令需要预先登录,所以这里还保留了上一步用来登录的代码)

//此处为具体逻辑
{
	//登录:
	{
		与第二步一样,省略。。。
	}
	//创建一个用来得到输出结果的ClientUser
	ClientUserForOutput cu_output;
	//运行clients命令:
	client.Run("clients", &cu_output);

	//测试打印出结果:
	for (auto s : cu_output.results)
	{
		std::cout << "输出:" << s << std::endl;
	}
}

输出:

User yaksue logged in.
输出:Client testworkspace 2022/05/28 root E:testworkspace 'Created by testuser1. '
输出:Client TestYaksueWorkSpace 2022/05/28 root d:TestTestP4WorkSpace 'Created by yaksue. '

看来这两个输入确实已经被存到 results 里。后续可以解析这些字符串来获得更具体的信息。

4. 创建新Changelist(学习InputData)

这个操作相对麻烦点。
回忆在之前的博客《学习使用命令行执行P4操作》中创建Changelist时,Changelist的一些信息是在文本编辑器中指定的。

那么现在在C++代码中,怎么指定呢?
——用 ClientUser::InputData( StrBuf *, Error * ) 来指定用户输入!

这里,我将主要参考UE引擎中PerforceConnection.cpp的代码来创建一个可以填写Changelist的描述文本的ClientUser:

//专门用于创建changelist的ClientUser类
class ClientUserForCreateCL : public ClientUser
{
public:
	//输入的ChangeList的描述:
	std::string Description;
	//正在运行的ClientAPI:
	ClientApi* P4Client;
	
	//P4API 调用"change -i"命令时触发
	void InputData(StrBuf* OutBuffer, Error* OutError)
	{
		OutBuffer->Append("Change:tnewnn");
		OutBuffer->Append("Client:t");
		OutBuffer->Append(P4Client->GetClient().Text());
		OutBuffer->Append("nn");
		OutBuffer->Append("User:t");
		OutBuffer->Append(P4Client->GetUser().Text());
		OutBuffer->Append("nn");
		OutBuffer->Append("Status:tnewnn");
		OutBuffer->Append("Description:n");
		{
			OutBuffer->Append("t");
			OutBuffer->Append(Description.c_str());
			OutBuffer->Append("n");			
		}
		OutBuffer->Append("n");
		OutBuffer->Append("Files:nn");
	}

	//P4命令输出结果时调用:
	void OutputInfo(char level, const char* data) override
	{
		std::cout << "创建CL时输出:" << data << std::endl;
	}
};

main函数中的逻辑改为:

//此处为具体逻辑
{
	//登录:
	{
		与第二步一样,省略。。。
	}
	
	//设置WorkSpace:
	client.SetClient("TestYaksueWorkSpace");

	//创建一个用于创建CL的ClientUser并指定描述
	ClientUserForCreateCL cu_cl;
	cu_cl.P4Client = &client;
	cu_cl.Description = "TestCreateCLInCpp";

	//设置 change 命令的参数
	const char* ArgV[] = { "-i" };
	client.SetArgv(1, const_cast(ArgV));
	//运行change命令
	client.Run("change", &cu_cl);
}

输出:

User yaksue logged in.
创建CL时输出:Change 8 created.

可以在P4V中确认,Changelist确实已经创建:

总结

P4的C++API 主要是通过 ClientApi 来管理的,它可以:

  • 设置WorkSpace(SetClient)
  • 设置用户(SetUser)
  • 运行命令(Run)
  • 等等。。。见 ClientApi methods

其中最重要的便是 Run,它相当于运行一个P4命令行(P4命令行更具体讨论可见《学习使用命令行执行P4操作》)。而命令行的参数可提前由SetArgv来指定。

而 Run时还需要指定一个ClientUser。一个ClientUser就像一个面对命令行的用户,你可以继承ClientUser基类实现自己的子类,从而实现自定义的行为,比如:

  • 指定如何面对输出(OutputInfo)
  • 指定如何填写密码(Prompt)
  • 指定如何填写输入文本(InputData)
  • 等等。。。见 ClientUser methods
完整代码

直到本篇第四步(创建新Changelist),完整的代码是:

#include 


#include "p4/clientapi.h"
#include "p4/p4libs.h"

#include 
//定义一个自己的ClientUser类
class MyClientUser : public ClientUser
{
public:
	//存储返回的数据:
	std::vector> data;

	//会被P4API处理标签化的输出时调用:
	virtual void OutputStat(StrDict* varList) override
	{
		StrRef Var, Value;
		//添加所有的键值对:
		for (int Index = 0; varList->GetVar(Index, Var, Value); Index++)
		{
			std::string tagName = Var.Text();
			std::string tagValue = Value.Text();
			data.push_back(std::make_pair(tagName, tagValue));
		}
	}
};

//定义一个用来登录的ClientUser类
class ClientUserForLogin : public ClientUser
{
public:
	std::string password; //用于登录的密码

	//会被P4API调用来得到用户输入
	virtual void Prompt(const StrPtr& InMessage, StrBuf& OutPrompt, int NoEcho, Error* InError) override
	{
		OutPrompt.Set(password.c_str());
	}
};
//定义用来获取输出的ClientUser类
class ClientUserForOutput : public ClientUser
{
public:
	//存储命令执行的结果:
	std::vector results; 

	//P4命令输出结果时调用:
	void OutputInfo(char level, const char* data) override
	{
		results.push_back(data);
	}
};
//专门用于创建changelist的ClientUser类
class ClientUserForCreateCL : public ClientUser
{
public:
	//输入的ChangeList的描述:
	std::string Description;
	//正在运行的ClientAPI:
	ClientApi* P4Client;
	
	//P4API 调用"change -i"命令时触发
	void InputData(StrBuf* OutBuffer, Error* OutError)
	{
		OutBuffer->Append("Change:tnewnn");
		OutBuffer->Append("Client:t");
		OutBuffer->Append(P4Client->GetClient().Text());
		OutBuffer->Append("nn");
		OutBuffer->Append("User:t");
		OutBuffer->Append(P4Client->GetUser().Text());
		OutBuffer->Append("nn");
		OutBuffer->Append("Status:tnewnn");
		OutBuffer->Append("Description:n");
		{
			OutBuffer->Append("t");
			OutBuffer->Append(Description.c_str());
			OutBuffer->Append("n");			
		}
		OutBuffer->Append("n");
		OutBuffer->Append("Files:nn");
	}

	//P4命令输出结果时调用:
	void OutputInfo(char level, const char* data) override
	{
		std::cout << "创建CL时输出:" << data << std::endl;
	}
};

int main()
{
	ClientApi client;	//调用API的结构
	Error e;			//用于容纳错误信息的结构

	//初始化库
	P4Libraries::Initialize(P4LIBRARIES_INIT_ALL, &e);
	//初始化API
	client.Init(&e);

	//此处为具体逻辑
	{
		//登录:
		{
			//设置用户名:
			client.SetUser("yaksue");

			//创建个用来登录的ClientUser并指定密码:
			ClientUserForLogin cu_login;
			cu_login.password = 此处填写用户的密码!;

			//设置login命令的参数:
			const char* ArgV[] = { "-a" };
			client.SetArgv(1, const_cast(ArgV));
			//运行login命令:
			client.Run("login", &cu_login);
		}
		
		//设置WorkSpace:
		client.SetClient("TestYaksueWorkSpace");

		//创建一个用于创建CL的ClientUser并指定描述
		ClientUserForCreateCL cu_cl;
		cu_cl.P4Client = &client;
		cu_cl.Description = "TestCreateCLInCpp";

		//设置 change 命令的参数
		const char* ArgV[] = { "-i" };
		client.SetArgv(1, const_cast(ArgV));
		//运行change命令
		client.Run("change", &cu_cl);
	}

	//API结束
	client.Final(&e);
	//库关闭
	P4Libraries::Shutdown(P4LIBRARIES_INIT_ALL, &e);

	return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934367.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号