2

I want to wrap a function, that is to create a new function such that it would automatically pass some arguments to the old function, like Python's partial functions. The arguments passed are the ones defined in the callee and not the caller. The important thing is that I don't want to refer to each of them explicitly (define them twice).

That is really done to save typing-in the same flags to complicated functions while allowing customization.

For example, in Python, I would do:

call_with_x=partial(call,x=1)

Or maybe use **kw and pass it to the callee in some cases.


This is my best try (based on Wrapper function in PowerShell: Pass remaining parameters):

function Let
{
    [CmdletBinding()]
Param([parameter(mandatory=$true, position=0)][string]$Option,
    [parameter(mandatory=$false, position=1, ValueFromRemainingArguments=$true)]$Remaining)
Get @Remaining
}

function Get
{
    [CmdletBinding()]
        Param([parameter(mandatory=$false, position=0)][string]$OptionA,
[parameter(mandatory=$true, position=1)][string]$OptionB)
Write-Host $OptionA, $OptionB
}

But Let -Option c -OptionA 1

Prints -OptionA 1 which is obviously not what I intended.

8
  • 1
    What is the intentions? You would only get -OptionA 1 cause you're only splatting that parameter. Maybe I'm not understanding what you're asking for, but you can splat all the parameters using @PSBoundParameters. The only issue would be that the "callee" function would have to have the same parameters as the caller function. Commented Jan 13, 2023 at 17:29
  • I would like the callee to have its optionA and optionB filled by args supplied to the caller. Notice that they don't have the same signature. PSBoundParameters seems like a good direction. Commented Jan 13, 2023 at 17:32
  • 1
    if you define $Remaining as [hashtable] without a need for ValueFromRemainingArguments then Get @Remaining would work without problems as long as the hashtable being passed as argument has Keys matching the parameters form Get. This would also mean when calling let you're actually building a hash with the parameters as Keys... Commented Jan 13, 2023 at 19:25
  • 1
    the DynamicParam idea is not bad either, you could build the remaining parameters at runtime by reading the AST of Get (untested but I think that should work) Commented Jan 13, 2023 at 19:33
  • 1
    (Get-Command Get).Parameters gives you all the parameters without the need for the AST. Commented Jan 13, 2023 at 20:00

2 Answers 2

3

If you don't require "advanced" function features from CmdletBinding(), then you can get away with using $args for this:

# simple function parameters are positional and named, but not mandatory
function let {
  Param($OptionA,$OptionB)

  write-host "OptionA=$OptionA"
  write-host "OptionB=$OptionB"

  get @args
}

function get {
  Param($OptionC,$OptionD)

  write-host "OptionC=$OptionC"
  write-host "OptionD=$OptionD"
}

# not necessary to name or include parameters
let -OptionA A B -OptionC C D

OptionA=A
OptionB=B
OptionC=C
OptionD=D

# named parameters get assigned first, so the order is not too big a deal either
# these produce the same results as above:
let B -OptionA A D -OptionC C 
let -OptionD D -OptionC C A B

Any named parameters will not get positionally assigned to let

Any additional parameters, named or otherwise, will be forwarded to get

Sign up to request clarification or add additional context in comments.

Comments

-1

That was hard! For this you would need to add a bit of code in DynmaicParams and begin section of the function.

It might be possible to do it in an Attribute

function x{
[WrapperFor(y)]

Might do it Later.

function Get
{
    [CmdLetBinding()]
        Param([parameter(mandatory=$false, position=0)][string]$OptionA,
[parameter(mandatory=$false, position=1)][string]$OptionB)
Write-Host "opta",$OptionA
Write-Host "optb",$OptionB
}


function Let
{
    [CmdletBinding()]
Param([parameter(mandatory=$true, position=0)][string]$Option,[parameter(mandatory=$false, position=0)][string]$OptionB)


DynamicParam {
AddWrapper -For "Get" -To "Let"
}
Begin { 
    
   $params = GetRestOfParams "Get" $PSBoundParameters
}
Process {
    Get @params 
}
}

Needed code:

using namespace System.Management.Automation
function Empt
{
    [CmdletBinding()]
        Param([parameter(mandatory=$true, position=0)][string]$aaaa)
    1
}

function AddWrapper([parameter(mandatory=$true, position=0)][string]$For,[parameter(mandatory=$true, position=1)][string]$To) 
{
    $paramDictionary = [RuntimeDefinedParameterDictionary]::new()
    $paramset= $(Get-Command $For).Parameters.Values | %{[System.Management.Automation.RuntimeDefinedParameter]::new($_.Name,$_.ParameterType,$_.Attributes)}
    $paramsetlet= $(Get-Command empt).Parameters.Keys 
    $paramsetlet+= $(Get-Command $To).ScriptBlock.Ast.Body.ParamBlock.Parameters.Name | %{ $_.VariablePath.UserPath }
    $paramset | %{ if ( -not ($paramsetlet -contains $_.Name) ) {$paramDictionary.Add($_.Name,$_)}}
    return $paramDictionary
}
function GetRestOfParams($dst,$params)
{
    $dstorgparams=$(Get-Command $dst).Parameters.Keys
    $z= $params
    $z.Keys | %{ if ( -not ($dstorgparams -contains $_) ) {$z.Remove($_)} } | Out-Null
    return $z
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.